transform manipulator didnt follow the active face in some cases, also rotate about...
[blender.git] / source / blender / src / transform_manipulator.c
1 /**
2 * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2005 Blender Foundation
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifndef WIN32
39 #include <unistd.h>
40 #else
41 #include <io.h>
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "DNA_armature_types.h"
47 #include "DNA_action_types.h"
48 #include "DNA_curve_types.h"
49 #include "DNA_lattice_types.h"
50 #include "DNA_mesh_types.h"
51 #include "DNA_meta_types.h"
52 #include "DNA_object_types.h"
53 #include "DNA_particle_types.h"
54 #include "DNA_screen_types.h"
55 #include "DNA_scene_types.h"
56 #include "DNA_space_types.h"
57 #include "DNA_userdef_types.h"
58 #include "DNA_view3d_types.h"
59
60 #include "BKE_armature.h"
61 #include "BKE_global.h"
62 #include "BKE_lattice.h"
63 #include "BKE_object.h"
64 #include "BKE_particle.h"
65 #include "BKE_utildefines.h"
66
67 #include "BLI_arithb.h"
68 #include "BLI_editVert.h"
69
70 #include "BIF_editarmature.h"
71 #include "BIF_gl.h"
72 #include "BIF_mywindow.h"
73 #include "BIF_resources.h"
74 #include "BIF_screen.h"
75 #include "BIF_space.h"
76 #include "BIF_transform.h"
77 #include "BIF_editmesh.h"
78 #include "BIF_editparticle.h"
79
80 #include "BSE_edit.h"
81 #include "BSE_view.h"
82 #include "BDR_drawobject.h"
83
84 #include "blendef.h"
85 #include "transform.h"
86
87 /* return codes for select, and drawing flags */
88
89 #define MAN_TRANS_X             1
90 #define MAN_TRANS_Y             2
91 #define MAN_TRANS_Z             4
92 #define MAN_TRANS_C             7
93
94 #define MAN_ROT_X               8
95 #define MAN_ROT_Y               16
96 #define MAN_ROT_Z               32
97 #define MAN_ROT_V               64
98 #define MAN_ROT_T               128
99 #define MAN_ROT_C               248
100
101 #define MAN_SCALE_X             256
102 #define MAN_SCALE_Y             512
103 #define MAN_SCALE_Z             1024
104 #define MAN_SCALE_C             1792
105
106 /* color codes */
107
108 #define MAN_RGB         0
109 #define MAN_GHOST       1
110 #define MAN_MOVECOL     2
111
112 /* GLOBAL VARIABLE THAT SHOULD MOVED TO SCREEN MEMBER OR SOMETHING  */
113 extern TransInfo Trans;
114
115
116 static int is_mat4_flipped(float mat[][4])
117 {
118         float vec[3];
119         
120         Crossf(vec, mat[0], mat[1]);
121         if( Inpf(vec, mat[2]) < 0.0 ) return 1;
122         return 0;
123 }       
124
125 /* transform widget center calc helper for below */
126 static void calc_tw_center(float *co)
127 {
128         float *twcent= G.scene->twcent;
129         float *min= G.scene->twmin;
130         float *max= G.scene->twmax;
131         
132         DO_MINMAX(co, min, max);
133         VecAddf(twcent, twcent, co);
134 }
135
136 static void protectflag_to_drawflags(short protectflag, short *drawflags)
137 {
138         if(protectflag & OB_LOCK_LOCX)
139                 *drawflags &= ~MAN_TRANS_X;
140         if(protectflag & OB_LOCK_LOCY)
141                 *drawflags &= ~MAN_TRANS_Y;
142         if(protectflag & OB_LOCK_LOCZ)
143                 *drawflags &= ~MAN_TRANS_Z;
144         
145         if(protectflag & OB_LOCK_ROTX)
146                 *drawflags &= ~MAN_ROT_X;
147         if(protectflag & OB_LOCK_ROTY)
148                 *drawflags &= ~MAN_ROT_Y;
149         if(protectflag & OB_LOCK_ROTZ)
150                 *drawflags &= ~MAN_ROT_Z;
151
152         if(protectflag & OB_LOCK_SCALEX)
153                 *drawflags &= ~MAN_SCALE_X;
154         if(protectflag & OB_LOCK_SCALEY)
155                 *drawflags &= ~MAN_SCALE_Y;
156         if(protectflag & OB_LOCK_SCALEZ)
157                 *drawflags &= ~MAN_SCALE_Z;
158 }
159
160 /* for pose mode */
161 static void stats_pose(View3D *v3d, bPoseChannel *pchan, float *normal, float *plane)
162 {
163         Bone *bone= pchan->bone;
164         
165         if(bone) {
166                 if (bone->flag & BONE_TRANSFORM) {
167                         calc_tw_center(pchan->pose_head);
168                         protectflag_to_drawflags(pchan->protectflag, &v3d->twdrawflag);
169
170                         VecAddf(normal, normal, pchan->pose_mat[2]);
171                         VecAddf(plane, plane, pchan->pose_mat[1]);
172                 }
173         }
174 }
175
176 /* only counts the parent selection, and tags transform flag */
177 /* bad call... should re-use method from transform_conversion once */
178 static void count_bone_select(TransInfo *t, bArmature *arm, ListBase *lb, int do_it) 
179 {
180         Bone *bone;
181         int do_next;
182         
183         for(bone= lb->first; bone; bone= bone->next) {
184                 bone->flag &= ~BONE_TRANSFORM;
185                 do_next= do_it;
186                 if(do_it) {
187                         if(bone->layer & arm->layer) {
188                                 if (bone->flag & BONE_SELECTED) {
189                                         /* We don't let connected children get "grabbed" */
190                                         if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_CONNECTED)==0 ) {
191                                                 bone->flag |= BONE_TRANSFORM;
192                                                 t->total++;
193                                                 do_next= 0;     // no transform on children if one parent bone is selected
194                                         }
195                                 }
196                         }
197                 }
198                 count_bone_select(t, arm, &bone->childbase, do_next);
199         }
200 }
201
202 /* centroid, boundbox, of selection */
203 /* returns total items selected */
204 int calc_manipulator_stats(ScrArea *sa)
205 {
206         extern ListBase editNurb;
207         TransInfo *t;
208         View3D *v3d= sa->spacedata.first;
209         Base *base;
210         Object *ob= OBACT;
211         float normal[3]={0.0, 0.0, 0.0};
212         float plane[3]={0.0, 0.0, 0.0};
213         int a, totsel=0;
214
215         t = BIF_GetTransInfo();
216         
217         /* transform widget matrix */
218         Mat4One(v3d->twmat);
219         
220         v3d->twdrawflag= 0xFFFF;
221         
222         /* transform widget centroid/center */
223         G.scene->twcent[0]= G.scene->twcent[1]= G.scene->twcent[2]= 0.0f;
224         INIT_MINMAX(G.scene->twmin, G.scene->twmax);
225         
226         if(G.obedit) {
227                 ob= G.obedit;
228                 if((ob->lay & G.vd->lay)==0) return 0;
229
230                 if(G.obedit->type==OB_MESH) {
231                         EditMesh *em = G.editMesh;
232                         EditVert *eve;
233                         EditSelection ese;
234                         float vec[3]= {0,0,0};
235                         
236                         /* USE LAST SELECTE WITH ACTIVE */
237                         if (G.vd->around==V3D_ACTIVE && EM_get_actSelection(&ese)) {
238                                 EM_editselection_center(vec, &ese);
239                                 calc_tw_center(vec);
240                                 totsel= 1;
241                         } else {
242                                 /* do vertices for center, and if still no normal found, use vertex normals */
243                                 for(eve= em->verts.first; eve; eve= eve->next) {
244                                         if(eve->f & SELECT) {
245                                                 totsel++;
246                                                 calc_tw_center(eve->co);
247                                         }
248                                 }
249                         }
250                 } /* end editmesh */
251                 else if (G.obedit->type==OB_ARMATURE){
252                         bArmature *arm= G.obedit->data;
253                         EditBone *ebo;
254                         for (ebo=G.edbo.first;ebo;ebo=ebo->next){
255                                 if(ebo->layer & arm->layer) {
256                                         if (ebo->flag & BONE_TIPSEL) {
257                                                 calc_tw_center(ebo->tail);
258                                                 totsel++;
259                                         }
260                                         if (ebo->flag & BONE_ROOTSEL) {
261                                                 calc_tw_center(ebo->head);
262                                                 totsel++;
263                                         }
264                                 }
265                         }
266                 }
267                 else if ELEM3(G.obedit->type, OB_CURVE, OB_SURF, OB_FONT) {
268                         Nurb *nu;
269                         BezTriple *bezt;
270                         BPoint *bp;
271                         
272                         nu= editNurb.first;
273                         while(nu) {
274                                 if((nu->type & 7)==CU_BEZIER) {
275                                         bezt= nu->bezt;
276                                         a= nu->pntsu;
277                                         while(a--) {
278                                                 /* exception */
279                                                 if( (bezt->f1 & SELECT) + (bezt->f2 & SELECT) + (bezt->f3 & SELECT) > SELECT ) {
280                                                         calc_tw_center(bezt->vec[1]);
281                                                         totsel++;
282                                                 }
283                                                 else {
284                                                         if(bezt->f1) {
285                                                                 calc_tw_center(bezt->vec[0]);
286                                                                 totsel++;
287                                                         }
288                                                         if(bezt->f2) {
289                                                                 calc_tw_center(bezt->vec[1]);
290                                                                 totsel++;
291                                                         }
292                                                         if(bezt->f3) {
293                                                                 calc_tw_center(bezt->vec[2]);
294                                                                 totsel++;
295                                                         }
296                                                 }
297                                                 bezt++;
298                                         }
299                                 }
300                                 else {
301                                         bp= nu->bp;
302                                         a= nu->pntsu*nu->pntsv;
303                                         while(a--) {
304                                                 if(bp->f1 & SELECT) {
305                                                         calc_tw_center(bp->vec);
306                                                         totsel++;
307                                                 }
308                                                 bp++;
309                                         }
310                                 }
311                                 nu= nu->next;
312                         }
313                 }
314                 else if(G.obedit->type==OB_MBALL) {
315                         /* editmball.c */
316                         extern ListBase editelems;  /* go away ! */
317                         MetaElem *ml, *ml_sel=NULL;
318                 
319                         ml= editelems.first;
320                         while(ml) {
321                                 if(ml->flag & SELECT) {
322                                         calc_tw_center(&ml->x);
323                                         ml_sel = ml;
324                                         totsel++;
325                                 }
326                                 ml= ml->next;
327                         }
328                 }
329                 else if(G.obedit->type==OB_LATTICE) {
330                         BPoint *bp;
331                         bp= editLatt->def;
332                         
333                         a= editLatt->pntsu*editLatt->pntsv*editLatt->pntsw;
334                         while(a--) {
335                                 if(bp->f1 & SELECT) {
336                                         calc_tw_center(bp->vec);
337                                         totsel++;
338                                 }
339                                 bp++;
340                         }
341                 }
342                 
343                 /* selection center */
344                 if(totsel) {
345                         VecMulf(G.scene->twcent, 1.0f/(float)totsel);   // centroid!
346                         Mat4MulVecfl(G.obedit->obmat, G.scene->twcent);
347                         Mat4MulVecfl(G.obedit->obmat, G.scene->twmin);
348                         Mat4MulVecfl(G.obedit->obmat, G.scene->twmax);
349                 }
350         }
351         else if(ob && (ob->flag & OB_POSEMODE)) {
352                 bArmature *arm= ob->data;
353                 bPoseChannel *pchan;
354                 int mode;
355                 
356                 if((ob->lay & G.vd->lay)==0) return 0;
357                 
358                 mode= Trans.mode;
359                 Trans.mode= TFM_ROTATION;       // mislead counting bones... bah
360                 
361                 /* count total, we use same method as transform will do */
362                 Trans.total= 0;
363                 count_bone_select(&Trans, arm, &arm->bonebase, 1);
364                 totsel= Trans.total;
365                 if(totsel) {
366                         /* use channels to get stats */
367                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
368                                 stats_pose(v3d, pchan, normal, plane);
369                         }
370                         //VecMulf(normal, -1.0);
371                         VecMulf(plane, -1.0);
372                         
373                         VecMulf(G.scene->twcent, 1.0f/(float)totsel);   // centroid!
374                         Mat4MulVecfl(ob->obmat, G.scene->twcent);
375                         Mat4MulVecfl(ob->obmat, G.scene->twmin);
376                         Mat4MulVecfl(ob->obmat, G.scene->twmax);
377                 }
378                 /* restore, mode can be TFM_INIT */
379                 Trans.mode= mode;
380         }
381         else if(G.f & (G_VERTEXPAINT + G_TEXTUREPAINT + G_WEIGHTPAINT + G_SCULPTMODE)) {
382                 ;
383         }
384         else if(G.f & G_PARTICLEEDIT) {
385                 ParticleSystem *psys=PE_get_current(OBACT);
386                 ParticleData *pa = psys->particles;
387                 ParticleEditKey *ek;
388                 int k;
389
390                 if(psys->edit){
391                         for(a=0; a<psys->totpart; a++,pa++){
392                                 if(pa->flag & PARS_HIDE) continue;
393                                 for(k=0, ek=psys->edit->keys[a]; k<pa->totkey; k++,ek++){
394                                         if(ek->flag & PEK_SELECT){
395                                                 calc_tw_center(ek->world_co);
396                                                 totsel++;
397                                         }
398                                 }
399                         }
400                         /* selection center */
401                         if(totsel)
402                                 VecMulf(G.scene->twcent, 1.0f/(float)totsel);   // centroid!
403                 }
404         }
405         else {
406                 
407                 /* we need the one selected object, if its not active */
408                 ob= OBACT;
409                 if(ob && !(ob->flag & SELECT)) ob= NULL;
410                 
411                 for(base= G.scene->base.first; base; base= base->next) {
412                         if TESTBASELIB(base) {
413                                 if(ob==NULL) 
414                                         ob= base->object;
415                                 calc_tw_center(base->object->obmat[3]);
416                                 protectflag_to_drawflags(base->object->protectflag, &v3d->twdrawflag);
417                                 totsel++;
418                         }
419                 }
420                 
421                 /* selection center */
422                 if(totsel) {
423                         VecMulf(G.scene->twcent, 1.0f/(float)totsel);   // centroid!
424                 }
425         }
426         
427         /* global, local or normal orientation? */
428         if(ob && totsel) {
429                 
430                 switch(v3d->twmode) {
431                 case V3D_MANIP_GLOBAL:
432                         strcpy(t->spacename, "global");
433                         break;
434                         
435                 case V3D_MANIP_NORMAL:
436                         if(G.obedit) {
437                                 float mat[3][3];
438                                 int type;
439                                 
440                                 strcpy(t->spacename, "normal");
441                                 
442                                 type = getTransformOrientation(normal, plane, (G.vd->around == V3D_ACTIVE));
443                                 
444                                 switch (type)
445                                 {
446                                         case ORIENTATION_NORMAL:
447                                                 if (createSpaceNormalTangent(mat, normal, plane) == 0)
448                                                 {
449                                                         type = ORIENTATION_NONE;
450                                                 }
451                                                 break;
452                                         case ORIENTATION_VERT:
453                                                 if (createSpaceNormal(mat, normal) == 0)
454                                                 {
455                                                         type = ORIENTATION_NONE;
456                                                 }
457                                                 break;
458                                         case ORIENTATION_EDGE:
459                                                 if (createSpaceNormalTangent(mat, normal, plane) == 0)
460                                                 {
461                                                         type = ORIENTATION_NONE;
462                                                 }
463                                                 break;
464                                         case ORIENTATION_FACE:
465                                                 if (createSpaceNormalTangent(mat, normal, plane) == 0)
466                                                 {
467                                                         type = ORIENTATION_NONE;
468                                                 }
469                                                 break;
470                                 }
471                                 
472                                 if (type == ORIENTATION_NONE)
473                                 {
474                                         Mat4One(v3d->twmat);
475                                 }
476                                 else
477                                 {
478                                         Mat4CpyMat3(v3d->twmat, mat);
479                                 }
480                                 break;
481                         }
482                         /* pose mode is a bit weird, so keep it separated */
483                         else if (ob->flag & OB_POSEMODE)
484                         {
485                                 strcpy(t->spacename, "normal");
486                                 if(normal[0]!=0.0 || normal[1]!=0.0 || normal[2]!=0.0) {
487                                         float imat[3][3], mat[3][3];
488                                         
489                                         /* we need the transpose of the inverse for a normal... */
490                                         Mat3CpyMat4(imat, ob->obmat);
491                                         
492                                         Mat3Inv(mat, imat);
493                                         Mat3Transp(mat);
494                                         Mat3MulVecfl(mat, normal);
495                                         Mat3MulVecfl(mat, plane);
496
497                                         Normalize(normal);
498                                         if(0.0==Normalize(plane)) VECCOPY(plane, mat[1]);
499                                         
500                                         VECCOPY(mat[2], normal);
501                                         Crossf(mat[0], normal, plane);
502                                         Crossf(mat[1], mat[2], mat[0]);
503                                         
504                                         Mat4CpyMat3(v3d->twmat, mat);
505                                         Mat4Ortho(v3d->twmat);
506
507                                         break;
508                                 }
509                         }
510                         /* no break we define 'normal' as 'local' in Object mode */
511                 case V3D_MANIP_LOCAL:
512                         strcpy(t->spacename, "local");
513                         Mat4CpyMat4(v3d->twmat, ob->obmat);
514                         Mat4Ortho(v3d->twmat);
515                         break;
516                         
517                 case V3D_MANIP_VIEW:
518                         {
519                                 float mat[3][3];
520                                 strcpy(t->spacename, "view");
521                                 Mat3CpyMat4(mat, v3d->viewinv);
522                                 Mat3Ortho(mat);
523                                 Mat4CpyMat3(v3d->twmat, mat);
524                         }
525                         break;
526                 default: /* V3D_MANIP_CUSTOM */
527                         applyTransformOrientation();
528                         break;
529                 }
530                 
531         }
532            
533         return totsel;
534 }
535
536 /* ******************** DRAWING STUFFIES *********** */
537
538 static float screen_aligned(float mat[][4])
539 {
540         float vec[3], size;
541         
542         VECCOPY(vec, mat[0]);
543         size= Normalize(vec);
544         
545         glTranslatef(mat[3][0], mat[3][1], mat[3][2]);
546         
547         /* sets view screen aligned */
548         glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]);
549         
550         return size;
551 }
552
553
554 /* radring = radius of donut rings
555    radhole = radius hole
556    start = starting segment (based on nrings)
557    end   = end segment
558    nsides = amount of points in ring
559    nrigns = amount of rings
560 */
561 static void partial_donut(float radring, float radhole, int start, int end, int nsides, int nrings)
562 {
563         float theta, phi, theta1;
564         float cos_theta, sin_theta;
565         float cos_theta1, sin_theta1;
566         float ring_delta, side_delta;
567         int i, j, docaps= 1;
568         
569         if(start==0 && end==nrings) docaps= 0;
570         
571         ring_delta= 2.0f*(float)M_PI/(float)nrings;
572         side_delta= 2.0f*(float)M_PI/(float)nsides;
573         
574         theta= (float)M_PI+0.5f*ring_delta;
575         cos_theta= (float)cos(theta);
576         sin_theta= (float)sin(theta);
577         
578         for(i= nrings - 1; i >= 0; i--) {
579                 theta1= theta + ring_delta;
580                 cos_theta1= (float)cos(theta1);
581                 sin_theta1= (float)sin(theta1);
582                 
583                 if(docaps && i==start) {        // cap
584                         glBegin(GL_POLYGON);
585                         phi= 0.0;
586                         for(j= nsides; j >= 0; j--) {
587                                 float cos_phi, sin_phi, dist;
588                                 
589                                 phi += side_delta;
590                                 cos_phi= (float)cos(phi);
591                                 sin_phi= (float)sin(phi);
592                                 dist= radhole + radring * cos_phi;
593                                 
594                                 glVertex3f(cos_theta1 * dist, -sin_theta1 * dist,  radring * sin_phi);
595                         }
596                         glEnd();
597                 }
598                 if(i>=start && i<=end) {
599                         glBegin(GL_QUAD_STRIP);
600                         phi= 0.0;
601                         for(j= nsides; j >= 0; j--) {
602                                 float cos_phi, sin_phi, dist;
603                                 
604                                 phi += side_delta;
605                                 cos_phi= (float)cos(phi);
606                                 sin_phi= (float)sin(phi);
607                                 dist= radhole + radring * cos_phi;
608                                 
609                                 glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi);
610                                 glVertex3f(cos_theta * dist, -sin_theta * dist,  radring * sin_phi);
611                         }
612                         glEnd();
613                 }
614                 
615                 if(docaps && i==end) {  // cap
616                         glBegin(GL_POLYGON);
617                         phi= 0.0;
618                         for(j= nsides; j >= 0; j--) {
619                                 float cos_phi, sin_phi, dist;
620                                 
621                                 phi -= side_delta;
622                                 cos_phi= (float)cos(phi);
623                                 sin_phi= (float)sin(phi);
624                                 dist= radhole + radring * cos_phi;
625                                 
626                                 glVertex3f(cos_theta * dist, -sin_theta * dist,  radring * sin_phi);
627                         }
628                         glEnd();
629                 }
630                 
631                 
632                 theta= theta1;
633                 cos_theta= cos_theta1;
634                 sin_theta= sin_theta1;
635         }
636 }
637
638 /* three colors can be set;
639    grey for ghosting
640    moving: in transform theme color
641    else the red/green/blue
642 */
643 static void manipulator_setcolor(char axis, int colcode)
644 {
645         float vec[4];
646         char col[4];
647         
648         vec[3]= 0.7f; // alpha set on 0.5, can be glEnabled or not
649         
650         if(colcode==MAN_GHOST) {
651                 glColor4ub(0, 0, 0, 70);
652         }
653         else if(colcode==MAN_MOVECOL) {
654                 BIF_GetThemeColor3ubv(TH_TRANSFORM, col);
655                 glColor4ub(col[0], col[1], col[2], 128);
656         }
657         else {
658                 switch(axis) {
659                 case 'c':
660                         BIF_GetThemeColor3ubv(TH_TRANSFORM, col);
661                         if(G.vd->twmode == V3D_MANIP_LOCAL) {
662                                 col[0]= col[0]>200?255:col[0]+55;
663                                 col[1]= col[1]>200?255:col[1]+55;
664                                 col[2]= col[2]>200?255:col[2]+55;
665                         }
666                         else if(G.vd->twmode == V3D_MANIP_NORMAL) {
667                                 col[0]= col[0]<55?0:col[0]-55;
668                                 col[1]= col[1]<55?0:col[1]-55;
669                                 col[2]= col[2]<55?0:col[2]-55;
670                         }
671                         glColor4ub(col[0], col[1], col[2], 128);
672                         break;
673                 case 'x':
674                         glColor4ub(220, 0, 0, 128);
675                         break;
676                 case 'y':
677                         glColor4ub(0, 220, 0, 128);
678                         break;
679                 case 'z':
680                         glColor4ub(30, 30, 220, 128);
681                         break;
682                 }
683         }
684 }
685
686 /* viewmatrix should have been set OK, also no shademode! */
687 static void draw_manipulator_axes(int colcode, int flagx, int flagy, int flagz)
688 {
689         
690         /* axes */
691         if(flagx) {
692                 manipulator_setcolor('x', colcode);
693                 if(flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X);
694                 else if(flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X);
695                 glBegin(GL_LINES);
696                 glVertex3f(0.2f, 0.0f, 0.0f);
697                 glVertex3f(1.0f, 0.0f, 0.0f);
698                 glEnd();
699         }               
700         if(flagy) {
701                 if(flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y);
702                 else if(flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y);
703                 manipulator_setcolor('y', colcode);
704                 glBegin(GL_LINES);
705                 glVertex3f(0.0f, 0.2f, 0.0f);
706                 glVertex3f(0.0f, 1.0f, 0.0f);
707                 glEnd();
708         }               
709         if(flagz) {
710                 if(flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z);
711                 else if(flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z);
712                 manipulator_setcolor('z', colcode);
713                 glBegin(GL_LINES);
714                 glVertex3f(0.0f, 0.0f, 0.2f);
715                 glVertex3f(0.0f, 0.0f, 1.0f);
716                 glEnd();
717         }
718 }
719
720 /* only called while G.moving */
721 static void draw_manipulator_rotate_ghost(float mat[][4], int drawflags)
722 {
723         GLUquadricObj *qobj;
724         float size, phi, startphi, vec[3], svec[3], matt[4][4], cross[3], tmat[3][3];
725         int arcs= (G.rt!=2);
726         
727         glDisable(GL_DEPTH_TEST);
728
729         qobj= gluNewQuadric(); 
730         gluQuadricDrawStyle(qobj, GLU_FILL); 
731         
732         glColor4ub(0,0,0,64);
733         glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
734         glEnable(GL_BLEND);
735                 
736         /* we need both [4][4] transforms, Trans.mat seems to be premul, not post for mat[][4] */
737         Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3]
738         Mat4MulMat34(matt, Trans.mat, mat);
739
740         /* Screen aligned view rot circle */
741         if(drawflags & MAN_ROT_V) {
742                 
743                 /* prepare for screen aligned draw */
744                 glPushMatrix();
745                 size= screen_aligned(mat);
746         
747                 vec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]);
748                 vec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]);
749                 vec[2]= 0.0f;
750                 Normalize(vec);
751                 
752                 startphi= saacos( vec[1] );
753                 if(vec[0]<0.0) startphi= -startphi;
754                 
755                 phi= (float)fmod(180.0*Trans.val/M_PI, 360.0);
756                 if(phi > 180.0) phi-= 360.0;
757                 else if(phi<-180.0) phi+= 360.0;
758                 
759                 gluPartialDisk(qobj, 0.0, size, 32, 1, 180.0*startphi/M_PI, phi);
760                 
761                 glPopMatrix();
762         }
763         else if(arcs) {
764                 float imat[3][3], ivmat[3][3];
765                 /* try to get the start rotation */
766                 
767                 svec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]);
768                 svec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]);
769                 svec[2]= 0.0f;
770                 
771                 /* screen aligned vec transform back to manipulator space */
772                 Mat3CpyMat4(ivmat, G.vd->viewinv);
773                 Mat3CpyMat4(tmat, mat);
774                 Mat3Inv(imat, tmat);
775                 Mat3MulMat3(tmat, imat, ivmat);
776                 
777                 Mat3MulVecfl(tmat, svec);       // tmat is used further on
778                 Normalize(svec);
779         }       
780         
781         mymultmatrix(mat);      // aligns with original widget
782         
783         /* Z disk */
784         if(drawflags & MAN_ROT_Z) {
785                 if(arcs) {
786                         /* correct for squeezed arc */
787                         svec[0]+= tmat[2][0];
788                         svec[1]+= tmat[2][1];
789                         Normalize(svec);
790                         
791                         startphi= (float)atan2(svec[0], svec[1]);
792                 }
793                 else startphi= 0.5f*(float)M_PI;
794                 
795                 VECCOPY(vec, mat[0]);   // use x axis to detect rotation
796                 Normalize(vec);
797                 Normalize(matt[0]);
798                 phi= saacos( Inpf(vec, matt[0]) );
799                 if(phi!=0.0) {
800                         Crossf(cross, vec, matt[0]);    // results in z vector
801                         if(Inpf(cross, mat[2]) > 0.0) phi= -phi;
802                         gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*(phi)/M_PI);
803                 }
804         }
805         /* X disk */
806         if(drawflags & MAN_ROT_X) {
807                 if(arcs) {
808                         /* correct for squeezed arc */
809                         svec[1]+= tmat[2][1];
810                         svec[2]+= tmat[2][2];
811                         Normalize(svec);
812                         
813                         startphi= (float)(M_PI + atan2(svec[2], -svec[1]));
814                 }
815                 else startphi= 0.0f;
816                 
817                 VECCOPY(vec, mat[1]);   // use y axis to detect rotation
818                 Normalize(vec);
819                 Normalize(matt[1]);
820                 phi= saacos( Inpf(vec, matt[1]) );
821                 if(phi!=0.0) {
822                         Crossf(cross, vec, matt[1]);    // results in x vector
823                         if(Inpf(cross, mat[0]) > 0.0) phi= -phi;
824                         glRotatef(90.0, 0.0, 1.0, 0.0);
825                         gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI);
826                         glRotatef(-90.0, 0.0, 1.0, 0.0);
827                 }
828         }       
829         /* Y circle */
830         if(drawflags & MAN_ROT_Y) {
831                 if(arcs) {
832                         /* correct for squeezed arc */
833                         svec[0]+= tmat[2][0];
834                         svec[2]+= tmat[2][2];
835                         Normalize(svec);
836                         
837                         startphi= (float)(M_PI + atan2(-svec[0], svec[2]));
838                 }
839                 else startphi= (float)M_PI;
840                 
841                 VECCOPY(vec, mat[2]);   // use z axis to detect rotation
842                 Normalize(vec);
843                 Normalize(matt[2]);
844                 phi= saacos( Inpf(vec, matt[2]) );
845                 if(phi!=0.0) {
846                         Crossf(cross, vec, matt[2]);    // results in y vector
847                         if(Inpf(cross, mat[1]) > 0.0) phi= -phi;
848                         glRotatef(-90.0, 1.0, 0.0, 0.0);
849                         gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI);
850                         glRotatef(90.0, 1.0, 0.0, 0.0);
851                 }
852         }
853         
854         glDisable(GL_BLEND);
855         myloadmatrix(G.vd->viewmat);
856 }
857
858 static void draw_manipulator_rotate(float mat[][4], int moving, int drawflags, int combo)
859 {
860         GLUquadricObj *qobj; 
861         double plane[4];
862         float size, vec[3], unitmat[4][4];
863         float cywid= 0.33f*0.01f*(float)U.tw_handlesize;        
864         float cusize= cywid*0.65f;
865         int arcs= (G.rt!=2);
866         int colcode;
867         
868         if(moving) colcode= MAN_MOVECOL;
869         else colcode= MAN_RGB;
870         
871         /* when called while moving in mixed mode, do not draw when... */
872         if((drawflags & MAN_ROT_C)==0) return;
873         
874         /* Init stuff */
875         glDisable(GL_DEPTH_TEST);
876         Mat4One(unitmat);
877
878         qobj= gluNewQuadric();
879         gluQuadricDrawStyle(qobj, GLU_FILL); 
880         
881         /* prepare for screen aligned draw */
882         VECCOPY(vec, mat[0]);
883         size= Normalize(vec);
884         glPushMatrix();
885         glTranslatef(mat[3][0], mat[3][1], mat[3][2]);
886         
887         if(arcs) {
888                 /* clipplane makes nice handles, calc here because of multmatrix but with translate! */
889                 VECCOPY(plane, G.vd->viewinv[2]);
890                 plane[3]= -0.02*size; // clip just a bit more
891                 glClipPlane(GL_CLIP_PLANE0, plane);
892         }
893         /* sets view screen aligned */
894         glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]);
895         
896         /* Screen aligned help circle */
897         if(arcs) {
898                 if((G.f & G_PICKSEL)==0) {
899                         BIF_ThemeColorShade(TH_BACK, -30);
900                         drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat);
901                 }
902         }
903         /* Screen aligned view rot circle */
904         if(drawflags & MAN_ROT_V) {
905                 if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V);
906                 BIF_ThemeColor(TH_TRANSFORM);
907                 drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat);
908                 
909                 if(moving) {    
910                         float vec[3];
911                         vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]);
912                         vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]);
913                         vec[2]= 0.0f;
914                         Normalize(vec);
915                         VecMulf(vec, 1.2f*size);
916                         glBegin(GL_LINES);
917                         glVertex3f(0.0f, 0.0f, 0.0f);
918                         glVertex3fv(vec);
919                         glEnd();
920                 }
921         }
922         glPopMatrix();
923         
924         /* apply the transform delta */
925         if(moving) {
926                 float matt[4][4];
927                 Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3]
928                 Mat4MulMat34(matt, Trans.mat, mat);
929                 mymultmatrix(matt);
930                 glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW);
931         }
932         else {
933                 glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW);
934                 mymultmatrix(mat);
935         }
936         
937         /* axes */
938         if(arcs==0) {
939                 if(!(G.f & G_PICKSEL)) {
940                         if( (combo & V3D_MANIP_SCALE)==0) {
941                                 /* axis */
942                                 glBegin(GL_LINES);
943                                 if( (drawflags & MAN_ROT_X) || (moving && (drawflags & MAN_ROT_Z)) ) {
944                                         manipulator_setcolor('x', colcode);
945                                         glVertex3f(0.2f, 0.0f, 0.0f);
946                                         glVertex3f(1.0f, 0.0f, 0.0f);
947                                 }               
948                                 if( (drawflags & MAN_ROT_Y) || (moving && (drawflags & MAN_ROT_X)) ) {
949                                         manipulator_setcolor('y', colcode);
950                                         glVertex3f(0.0f, 0.2f, 0.0f);
951                                         glVertex3f(0.0f, 1.0f, 0.0f);
952                                 }               
953                                 if( (drawflags & MAN_ROT_Z) || (moving && (drawflags & MAN_ROT_Y)) ) {
954                                         manipulator_setcolor('z', colcode);
955                                         glVertex3f(0.0f, 0.0f, 0.2f);
956                                         glVertex3f(0.0f, 0.0f, 1.0f);
957                                 }
958                                 glEnd();
959                         }
960                 }
961         }
962         
963         if(arcs==0 && moving) {
964                 
965                 /* Z circle */
966                 if(drawflags & MAN_ROT_Z) {
967                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
968                         manipulator_setcolor('z', colcode);
969                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
970                 }
971                 /* X circle */
972                 if(drawflags & MAN_ROT_X) {
973                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
974                         glRotatef(90.0, 0.0, 1.0, 0.0);
975                         manipulator_setcolor('x', colcode);
976                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
977                         glRotatef(-90.0, 0.0, 1.0, 0.0);
978                 }       
979                 /* Y circle */
980                 if(drawflags & MAN_ROT_Y) {
981                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
982                         glRotatef(-90.0, 1.0, 0.0, 0.0);
983                         manipulator_setcolor('y', colcode);
984                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
985                         glRotatef(90.0, 1.0, 0.0, 0.0);
986                 }
987                 
988                 if(arcs) glDisable(GL_CLIP_PLANE0);
989         }
990         // donut arcs
991         if(arcs) {
992                 glEnable(GL_CLIP_PLANE0);
993                 
994                 /* Z circle */
995                 if(drawflags & MAN_ROT_Z) {
996                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
997                         manipulator_setcolor('z', colcode);
998                         partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48);
999                 }
1000                 /* X circle */
1001                 if(drawflags & MAN_ROT_X) {
1002                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
1003                         glRotatef(90.0, 0.0, 1.0, 0.0);
1004                         manipulator_setcolor('x', colcode);
1005                         partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48);
1006                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1007                 }       
1008                 /* Y circle */
1009                 if(drawflags & MAN_ROT_Y) {
1010                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
1011                         glRotatef(-90.0, 1.0, 0.0, 0.0);
1012                         manipulator_setcolor('y', colcode);
1013                         partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48);
1014                         glRotatef(90.0, 1.0, 0.0, 0.0);
1015                 }
1016                 
1017                 glDisable(GL_CLIP_PLANE0);
1018         }
1019         
1020         if(arcs==0) {
1021                 
1022                 /* Z handle on X axis */
1023                 if(drawflags & MAN_ROT_Z) {
1024                         glPushMatrix();
1025                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
1026                         manipulator_setcolor('z', colcode);
1027
1028                         partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64);
1029
1030                         glPopMatrix();
1031                 }       
1032
1033                 /* Y handle on X axis */
1034                 if(drawflags & MAN_ROT_Y) {
1035                         glPushMatrix();
1036                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
1037                         manipulator_setcolor('y', colcode);
1038                         
1039                         glRotatef(90.0, 1.0, 0.0, 0.0);
1040                         glRotatef(90.0, 0.0, 0.0, 1.0);
1041                         partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64);
1042                         
1043                         glPopMatrix();
1044                 }
1045                 
1046                 /* X handle on Z axis */
1047                 if(drawflags & MAN_ROT_X) {
1048                         glPushMatrix();
1049                         if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
1050                         manipulator_setcolor('x', colcode);
1051                         
1052                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1053                         glRotatef(90.0, 0.0, 0.0, 1.0);
1054                         partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64);
1055
1056                         glPopMatrix();
1057                 }
1058                 
1059         }
1060         
1061         /* restore */
1062         myloadmatrix(G.vd->viewmat);
1063         gluDeleteQuadric(qobj);
1064         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST);
1065         
1066 }
1067
1068 static void draw_manipulator_scale(float mat[][4], int moving, int drawflags, int combo, int colcode)
1069 {
1070         float cywid= 0.25f*0.01f*(float)U.tw_handlesize;        
1071         float cusize= cywid*0.75f, dz;
1072         
1073         /* when called while moving in mixed mode, do not draw when... */
1074         if((drawflags & MAN_SCALE_C)==0) return;
1075         
1076         glDisable(GL_DEPTH_TEST);
1077         
1078         /* not in combo mode */
1079         if( (combo & (V3D_MANIP_TRANSLATE|V3D_MANIP_ROTATE))==0) {
1080                 float size, unitmat[4][4];
1081                 
1082                 /* center circle, do not add to selection when shift is pressed (planar constraint)  */
1083                 if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_SCALE_C);
1084                 
1085                 manipulator_setcolor('c', colcode);
1086                 glPushMatrix();
1087                 size= screen_aligned(mat);
1088                 Mat4One(unitmat);
1089                 drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat);
1090                 glPopMatrix();
1091                 
1092                 dz= 1.0;
1093         }
1094         else dz= 1.0f-4.0f*cusize;
1095         
1096         if(moving) {
1097                 float matt[4][4];
1098                 
1099                 Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3]
1100                 Mat4MulMat34(matt, Trans.mat, mat);
1101                 mymultmatrix(matt);
1102                 glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW);
1103         }
1104         else {
1105                 mymultmatrix(mat);
1106                 glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW);
1107         }
1108         
1109         /* axis */
1110                 
1111         /* in combo mode, this is always drawn as first type */
1112         draw_manipulator_axes(colcode, drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z);
1113         
1114         /* Z cube */
1115         glTranslatef(0.0, 0.0, dz);
1116         if(drawflags & MAN_SCALE_Z) {
1117                 if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z);
1118                 manipulator_setcolor('z', colcode);
1119                 drawsolidcube(cusize);
1120         }       
1121         /* X cube */
1122         glTranslatef(dz, 0.0, -dz);
1123         if(drawflags & MAN_SCALE_X) {
1124                 if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_X);
1125                 manipulator_setcolor('x', colcode);
1126                 drawsolidcube(cusize);
1127         }       
1128         /* Y cube */
1129         glTranslatef(-dz, dz, 0.0);
1130         if(drawflags & MAN_SCALE_Y) {
1131                 if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y);
1132                 manipulator_setcolor('y', colcode);
1133                 drawsolidcube(cusize);
1134         }
1135         
1136         /* if shiftkey, center point as last, for selectbuffer order */
1137         if(G.f & G_PICKSEL) {
1138                 if(G.qual & LR_SHIFTKEY) {
1139                         glTranslatef(0.0, -dz, 0.0);
1140                         glLoadName(MAN_SCALE_C);
1141                         glBegin(GL_POINTS);
1142                         glVertex3f(0.0, 0.0, 0.0);
1143                         glEnd();
1144                 }
1145         }
1146         
1147         /* restore */
1148         myloadmatrix(G.vd->viewmat);
1149         
1150         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); 
1151         glFrontFace(GL_CCW);
1152 }
1153
1154
1155 static void draw_cone(GLUquadricObj *qobj, float len, float width)
1156 {
1157         glTranslatef(0.0, 0.0, -0.5f*len);
1158         gluCylinder(qobj, width, 0.0, len, 8, 1);
1159         gluQuadricOrientation(qobj, GLU_INSIDE);
1160         gluDisk(qobj, 0.0, width, 8, 1); 
1161         gluQuadricOrientation(qobj, GLU_OUTSIDE);
1162         glTranslatef(0.0, 0.0, 0.5f*len);
1163 }
1164
1165 static void draw_cylinder(GLUquadricObj *qobj, float len, float width)
1166 {
1167         
1168         width*= 0.8f;   // just for beauty
1169         
1170         glTranslatef(0.0, 0.0, -0.5f*len);
1171         gluCylinder(qobj, width, width, len, 8, 1);
1172         gluQuadricOrientation(qobj, GLU_INSIDE);
1173         gluDisk(qobj, 0.0, width, 8, 1); 
1174         gluQuadricOrientation(qobj, GLU_OUTSIDE);
1175         glTranslatef(0.0, 0.0, len);
1176         gluDisk(qobj, 0.0, width, 8, 1); 
1177         glTranslatef(0.0, 0.0, -0.5f*len);
1178 }
1179
1180
1181 static void draw_manipulator_translate(float mat[][4], int moving, int drawflags, int combo, int colcode)
1182 {
1183         GLUquadricObj *qobj; 
1184         float cylen= 0.01f*(float)U.tw_handlesize;
1185         float cywid= 0.25f*cylen, dz, size;
1186         float unitmat[4][4];
1187         
1188         /* when called while moving in mixed mode, do not draw when... */
1189         if((drawflags & MAN_TRANS_C)==0) return;
1190         
1191         if(moving) glTranslatef(Trans.vec[0], Trans.vec[1], Trans.vec[2]);
1192         glDisable(GL_DEPTH_TEST);
1193
1194         qobj= gluNewQuadric();
1195         gluQuadricDrawStyle(qobj, GLU_FILL); 
1196         
1197         /* center circle, do not add to selection when shift is pressed (planar constraint) */
1198         if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_TRANS_C);
1199         
1200         manipulator_setcolor('c', colcode);
1201         glPushMatrix();
1202         size= screen_aligned(mat);
1203         Mat4One(unitmat);
1204         drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat);
1205         glPopMatrix();
1206         
1207         /* and now apply matrix, we move to local matrix drawing */
1208         mymultmatrix(mat);
1209         
1210         /* axis */
1211         glLoadName(-1);
1212         
1213         // translate drawn as last, only axis when no combo with scale, or for ghosting
1214         if((combo & V3D_MANIP_SCALE)==0 || colcode==MAN_GHOST)
1215                 draw_manipulator_axes(colcode, drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z);
1216
1217         
1218         /* offset in combo mode, for rotate a bit more */
1219         if(combo & (V3D_MANIP_ROTATE)) dz= 1.0f+2.0f*cylen;
1220         else if(combo & (V3D_MANIP_SCALE)) dz= 1.0f+0.5f*cylen;
1221         else dz= 1.0f;
1222         
1223         /* Z Cone */
1224         glTranslatef(0.0, 0.0, dz);
1225         if(drawflags & MAN_TRANS_Z) {
1226                 if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z);
1227                 manipulator_setcolor('z', colcode);
1228                 draw_cone(qobj, cylen, cywid);
1229         }       
1230         /* X Cone */
1231         glTranslatef(dz, 0.0, -dz);
1232         if(drawflags & MAN_TRANS_X) {
1233                 if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_X);
1234                 glRotatef(90.0, 0.0, 1.0, 0.0);
1235                 manipulator_setcolor('x', colcode);
1236                 draw_cone(qobj, cylen, cywid);
1237                 glRotatef(-90.0, 0.0, 1.0, 0.0);
1238         }       
1239         /* Y Cone */
1240         glTranslatef(-dz, dz, 0.0);
1241         if(drawflags & MAN_TRANS_Y) {
1242                 if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y);
1243                 glRotatef(-90.0, 1.0, 0.0, 0.0);
1244                 manipulator_setcolor('y', colcode);
1245                 draw_cone(qobj, cylen, cywid);
1246         }
1247
1248         gluDeleteQuadric(qobj);
1249         myloadmatrix(G.vd->viewmat);
1250         
1251         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST);
1252         
1253 }
1254
1255 static void draw_manipulator_rotate_cyl(float mat[][4], int moving, int drawflags, int combo, int colcode)
1256 {
1257         GLUquadricObj *qobj;
1258         float size;
1259         float cylen= 0.01f*(float)U.tw_handlesize;
1260         float cywid= 0.25f*cylen;
1261         
1262         /* when called while moving in mixed mode, do not draw when... */
1263         if((drawflags & MAN_ROT_C)==0) return;
1264
1265         /* prepare for screen aligned draw */
1266         glPushMatrix();
1267         size= screen_aligned(mat);
1268         
1269         glDisable(GL_DEPTH_TEST);
1270
1271         qobj= gluNewQuadric(); 
1272         
1273         /* Screen aligned view rot circle */
1274         if(drawflags & MAN_ROT_V) {
1275                 float unitmat[4][4];
1276                 Mat4One(unitmat);
1277                 
1278                 if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V);
1279                 BIF_ThemeColor(TH_TRANSFORM);
1280                 drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat);
1281                 
1282                 if(moving) {
1283                         float vec[3];
1284                         vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]);
1285                         vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]);
1286                         vec[2]= 0.0f;
1287                         Normalize(vec);
1288                         VecMulf(vec, 1.2f*size);
1289                         glBegin(GL_LINES);
1290                         glVertex3f(0.0, 0.0, 0.0);
1291                         glVertex3fv(vec);
1292                         glEnd();
1293                 }
1294         }
1295         glPopMatrix();
1296         
1297         /* apply the transform delta */
1298         if(moving) {
1299                 float matt[4][4];
1300                 Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3]
1301                 if (Trans.flag & T_USES_MANIPULATOR) {
1302                         Mat4MulMat34(matt, Trans.mat, mat);
1303                 }
1304                 mymultmatrix(matt);
1305         }
1306         else {
1307                 mymultmatrix(mat);
1308         }
1309         
1310         glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW);
1311         
1312         /* axis */
1313         if( (G.f & G_PICKSEL)==0 ) {
1314                 
1315                 // only draw axis when combo didn't draw scale axes
1316                 if((combo & V3D_MANIP_SCALE)==0)
1317                         draw_manipulator_axes(colcode, drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z);
1318                 
1319                 /* only has to be set when not in picking */
1320                 gluQuadricDrawStyle(qobj, GLU_FILL); 
1321         }
1322         
1323         /* Z cyl */
1324         glTranslatef(0.0, 0.0, 1.0);
1325         if(drawflags & MAN_ROT_Z) {
1326                 if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z);
1327                 manipulator_setcolor('z', colcode);
1328                 draw_cylinder(qobj, cylen, cywid);
1329         }       
1330         /* X cyl */
1331         glTranslatef(1.0, 0.0, -1.0);
1332         if(drawflags & MAN_ROT_X) {
1333                 if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X);
1334                 glRotatef(90.0, 0.0, 1.0, 0.0);
1335                 manipulator_setcolor('x', colcode);
1336                 draw_cylinder(qobj, cylen, cywid);
1337                 glRotatef(-90.0, 0.0, 1.0, 0.0);
1338         }       
1339         /* Y cylinder */
1340         glTranslatef(-1.0, 1.0, 0.0);
1341         if(drawflags & MAN_ROT_Y) {
1342                 if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y);
1343                 glRotatef(-90.0, 1.0, 0.0, 0.0);
1344                 manipulator_setcolor('y', colcode);
1345                 draw_cylinder(qobj, cylen, cywid);
1346         }
1347         
1348         /* restore */
1349         
1350         gluDeleteQuadric(qobj);
1351         myloadmatrix(G.vd->viewmat);
1352         
1353         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST);
1354         
1355 }
1356
1357
1358 /* ********************************************* */
1359
1360 float get_drawsize(View3D *v3d, float *co)
1361 {
1362         ScrArea *sa = v3d->area;
1363         float size, vec[3], len1, len2;
1364         
1365         /* size calculus, depending ortho/persp settings, like initgrabz() */
1366         size= v3d->persmat[0][3]*co[0]+ v3d->persmat[1][3]*co[1]+ v3d->persmat[2][3]*co[2]+ v3d->persmat[3][3];
1367         
1368         VECCOPY(vec, v3d->persinv[0]);
1369         len1= Normalize(vec);
1370         VECCOPY(vec, v3d->persinv[1]);
1371         len2= Normalize(vec);
1372         
1373         size*= 0.01f*(len1>len2?len1:len2);
1374
1375         /* correct for window size to make widgets appear fixed size */
1376         if(sa->winx > sa->winy) size*= 1000.0f/(float)sa->winx;
1377         else size*= 1000.0f/(float)sa->winy;
1378
1379         return size;
1380 }
1381
1382 static float get_manipulator_drawsize(ScrArea *sa)
1383 {
1384         View3D *v3d= sa->spacedata.first;
1385         float size = get_drawsize(v3d, v3d->twmat[3]);
1386         
1387         size*= (float)U.tw_size;
1388
1389         return size;
1390 }
1391
1392 /* exported to transform_constraints.c */
1393 /* mat, vec = default orientation and location */
1394 /* type = transform type */
1395 /* axis = x, y, z, c */
1396 /* col: 0 = colored, 1 = moving, 2 = ghost */
1397 void draw_manipulator_ext(ScrArea *sa, int type, char axis, int col, float vec[3], float mat[][3])
1398 {
1399         int drawflags= 0;
1400         float mat4[4][4];
1401         int colcode;
1402         
1403         Mat4CpyMat3(mat4, mat);
1404         VECCOPY(mat4[3], vec);
1405         
1406         Mat4MulFloat3((float *)mat4, get_manipulator_drawsize(sa));
1407         
1408         glEnable(GL_BLEND);     // let's do it transparent by default
1409         if(col==0) colcode= MAN_RGB;
1410         else if(col==1) colcode= MAN_MOVECOL;
1411         else colcode= MAN_GHOST;
1412         
1413         
1414         if(type==TFM_ROTATION) {
1415                 if(axis=='x') drawflags= MAN_ROT_X;
1416                 else if(axis=='y') drawflags= MAN_ROT_Y;
1417                 else if(axis=='z') drawflags= MAN_ROT_Z;
1418                 else drawflags= MAN_ROT_C;
1419                 
1420                 draw_manipulator_rotate_cyl(mat4, col, drawflags, V3D_MANIP_ROTATE, colcode);
1421         }       
1422         else if(type==TFM_RESIZE) {
1423                 if(axis=='x') drawflags= MAN_SCALE_X;
1424                 else if(axis=='y') drawflags= MAN_SCALE_Y;
1425                 else if(axis=='z') drawflags= MAN_SCALE_Z;
1426                 else drawflags= MAN_SCALE_C;
1427
1428                 draw_manipulator_scale(mat4, col, drawflags, V3D_MANIP_SCALE, colcode);
1429         }       
1430         else {
1431                 if(axis=='x') drawflags= MAN_TRANS_X;
1432                 else if(axis=='y') drawflags= MAN_TRANS_Y;
1433                 else if(axis=='z') drawflags= MAN_TRANS_Z;
1434                 else drawflags= MAN_TRANS_C;
1435
1436                 draw_manipulator_translate(mat4, 0, drawflags, V3D_MANIP_TRANSLATE, colcode);
1437         }       
1438         
1439
1440         glDisable(GL_BLEND);
1441 }
1442
1443 /* main call, does calc centers & orientation too */
1444 /* uses global G.moving */
1445 static int drawflags= 0xFFFF;           // only for the calls below, belongs in scene...?
1446 void BIF_draw_manipulator(ScrArea *sa)
1447 {
1448         View3D *v3d= sa->spacedata.first;
1449         int totsel;
1450         
1451         if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return;
1452         if(G.moving && (G.moving & G_TRANSFORM_MANIP)==0) return;
1453         
1454         if(G.moving==0) {
1455                 v3d->twflag &= ~V3D_DRAW_MANIPULATOR;
1456                 
1457                 totsel= calc_manipulator_stats(sa);
1458                 if(totsel==0) return;
1459                 drawflags= v3d->twdrawflag;     /* set in calc_manipulator_stats */
1460
1461                 v3d->twflag |= V3D_DRAW_MANIPULATOR;
1462
1463                 /* now we can define center */
1464                 switch(v3d->around) {
1465                 case V3D_CENTER:
1466                 case V3D_ACTIVE:
1467                         v3d->twmat[3][0]= (G.scene->twmin[0] + G.scene->twmax[0])/2.0f;
1468                         v3d->twmat[3][1]= (G.scene->twmin[1] + G.scene->twmax[1])/2.0f;
1469                         v3d->twmat[3][2]= (G.scene->twmin[2] + G.scene->twmax[2])/2.0f;
1470                         if(v3d->around==V3D_ACTIVE && G.obedit==NULL) {
1471                                 Object *ob= OBACT;
1472                                 if(ob && !(ob->flag & OB_POSEMODE)) 
1473                                         VECCOPY(v3d->twmat[3], ob->obmat[3]);
1474                         }
1475                         break;
1476                 case V3D_LOCAL:
1477                 case V3D_CENTROID:
1478                         VECCOPY(v3d->twmat[3], G.scene->twcent);
1479                         break;
1480                 case V3D_CURSOR:
1481                         VECCOPY(v3d->twmat[3], give_cursor());
1482                         break;
1483                 }
1484                 
1485                 Mat4MulFloat3((float *)v3d->twmat, get_manipulator_drawsize(sa));
1486         }
1487         
1488         if(v3d->twflag & V3D_DRAW_MANIPULATOR) {
1489                 
1490                 if(v3d->twtype & V3D_MANIP_ROTATE) {
1491                         
1492                         /* rotate has special ghosting draw, for pie chart */
1493                         if(G.moving) draw_manipulator_rotate_ghost(v3d->twmat, drawflags);
1494                         
1495                         if(G.moving) glEnable(GL_BLEND);
1496                         
1497                         if(G.rt==3) {
1498                                 if(G.moving) draw_manipulator_rotate_cyl(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL);
1499                                 else draw_manipulator_rotate_cyl(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB);
1500                         }
1501                         else
1502                                 draw_manipulator_rotate(v3d->twmat, G.moving, drawflags, v3d->twtype);
1503                         
1504                         glDisable(GL_BLEND);
1505                 }
1506                 if(v3d->twtype & V3D_MANIP_SCALE) {
1507                         if(G.moving) {
1508                                 glEnable(GL_BLEND);
1509                                 draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST);
1510                                 draw_manipulator_scale(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL);
1511                                 glDisable(GL_BLEND);
1512                         }
1513                         else draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB);
1514                 }
1515                 if(v3d->twtype & V3D_MANIP_TRANSLATE) {
1516                         if(G.moving) {
1517                                 glEnable(GL_BLEND);
1518                                 draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST);
1519                                 draw_manipulator_translate(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL);
1520                                 glDisable(GL_BLEND);
1521                         }
1522                         else draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB);
1523                 }
1524         }
1525 }
1526
1527 static int manipulator_selectbuf(ScrArea *sa, float hotspot)
1528 {
1529         View3D *v3d= sa->spacedata.first;
1530         rctf rect;
1531         GLuint buffer[64];              // max 4 items per select, so large enuf
1532         short hits, mval[2];
1533         
1534         G.f |= G_PICKSEL;
1535         
1536         getmouseco_areawin(mval);
1537         rect.xmin= mval[0]-hotspot;
1538         rect.xmax= mval[0]+hotspot;
1539         rect.ymin= mval[1]-hotspot;
1540         rect.ymax= mval[1]+hotspot;
1541         
1542         /* get rid of overlay button matrix */
1543         persp(PERSP_VIEW);
1544         
1545         setwinmatrixview3d(sa->winx, sa->winy, &rect);
1546         Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat);
1547         
1548         glSelectBuffer( 64, buffer);
1549         glRenderMode(GL_SELECT);
1550         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1551         glPushName(-2);
1552         
1553         /* do the drawing */
1554         if(v3d->twtype & V3D_MANIP_ROTATE) {
1555                 if(G.rt==3) draw_manipulator_rotate_cyl(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype, MAN_RGB);
1556                 else draw_manipulator_rotate(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype);
1557         }
1558         if(v3d->twtype & V3D_MANIP_SCALE)
1559                 draw_manipulator_scale(v3d->twmat, 0, MAN_SCALE_C & v3d->twdrawflag, v3d->twtype, MAN_RGB);
1560         if(v3d->twtype & V3D_MANIP_TRANSLATE)
1561                 draw_manipulator_translate(v3d->twmat, 0, MAN_TRANS_C & v3d->twdrawflag, v3d->twtype, MAN_RGB);
1562         
1563         glPopName();
1564         hits= glRenderMode(GL_RENDER);
1565         
1566         G.f &= ~G_PICKSEL;
1567         setwinmatrixview3d(sa->winx, sa->winy, NULL);
1568         Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat);
1569         
1570         persp(PERSP_WIN);
1571         
1572         if(hits==1) return buffer[3];
1573         else if(hits>1) {
1574                 GLuint val, dep, mindep=0, mindeprot=0, minval=0, minvalrot=0;
1575                 int a;
1576                 
1577                 /* we compare the hits in buffer, but value centers highest */
1578                 /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */
1579
1580                 for(a=0; a<hits; a++) {
1581                         dep= buffer[4*a + 1];
1582                         val= buffer[4*a + 3];
1583                         
1584                         if(val==MAN_TRANS_C) return MAN_TRANS_C;
1585                         else if(val==MAN_SCALE_C) return MAN_SCALE_C;
1586                         else {
1587                                 if(val & MAN_ROT_C) {
1588                                         if(minvalrot==0 || dep<mindeprot) {
1589                                                 mindeprot= dep;
1590                                                 minvalrot= val;
1591                                         }
1592                                 }
1593                                 else {
1594                                         if(minval==0 || dep<mindep) {
1595                                                 mindep= dep;
1596                                                 minval= val;
1597                                         }
1598                                 }
1599                         }
1600                 }
1601                 
1602                 if(minval)
1603                         return minval;
1604                 else
1605                         return minvalrot;
1606         }
1607         return 0;
1608 }
1609
1610 /* return 0; nothing happened */
1611 int BIF_do_manipulator(ScrArea *sa)
1612 {
1613         View3D *v3d= sa->spacedata.first;
1614         int val;
1615         
1616         if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return 0;
1617         if(!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return 0;
1618         
1619         // find the hotspots first test narrow hotspot
1620         val= manipulator_selectbuf(sa, 0.5f*(float)U.tw_hotspot);
1621         if(val) {
1622                 checkFirstTime(); // TEMPORARY, check this before doing any transform call.
1623                 // drawflags still global, for drawing call above
1624                 drawflags= manipulator_selectbuf(sa, 0.2f*(float)U.tw_hotspot);
1625                 if(drawflags==0) drawflags= val;
1626
1627                 if (drawflags & MAN_TRANS_C) {
1628                         initManipulator(TFM_TRANSLATION);
1629                         switch(drawflags) {
1630                         case MAN_TRANS_C:
1631                                 break;
1632                         case MAN_TRANS_X:
1633                                 if(G.qual & LR_SHIFTKEY) {
1634                                         drawflags= MAN_TRANS_Y|MAN_TRANS_Z;
1635                                         BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z");
1636                                 }
1637                                 else
1638                                         BIF_setSingleAxisConstraint(v3d->twmat[0], " X");
1639                                 break;
1640                         case MAN_TRANS_Y:
1641                                 if(G.qual & LR_SHIFTKEY) {
1642                                         drawflags= MAN_TRANS_X|MAN_TRANS_Z;
1643                                         BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z");
1644                                 }
1645                                 else
1646                                         BIF_setSingleAxisConstraint(v3d->twmat[1], " Y");
1647                                 break;
1648                         case MAN_TRANS_Z:
1649                                 if(G.qual & LR_SHIFTKEY) {
1650                                         drawflags= MAN_TRANS_X|MAN_TRANS_Y;
1651                                         BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y");
1652                                 }
1653                                 else
1654                                         BIF_setSingleAxisConstraint(v3d->twmat[2], " Z");
1655                                 break;
1656                         }
1657                         ManipulatorTransform();
1658                 }
1659                 else if (drawflags & MAN_SCALE_C) {
1660                         initManipulator(TFM_RESIZE);
1661                         switch(drawflags) {
1662                         case MAN_SCALE_X:
1663                                 if(G.qual & LR_SHIFTKEY) {
1664                                         drawflags= MAN_SCALE_Y|MAN_SCALE_Z;
1665                                         BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z");
1666                                 }
1667                                 else
1668                                         BIF_setSingleAxisConstraint(v3d->twmat[0], " X");
1669                                 break;
1670                         case MAN_SCALE_Y:
1671                                 if(G.qual & LR_SHIFTKEY) {
1672                                         drawflags= MAN_SCALE_X|MAN_SCALE_Z;
1673                                         BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z");
1674                                 }
1675                                 else
1676                                         BIF_setSingleAxisConstraint(v3d->twmat[1], " Y");
1677                                 break;
1678                         case MAN_SCALE_Z:
1679                                 if(G.qual & LR_SHIFTKEY) {
1680                                         drawflags= MAN_SCALE_X|MAN_SCALE_Y;
1681                                         BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y");
1682                                 }
1683                                 else
1684                                         BIF_setSingleAxisConstraint(v3d->twmat[2], " Z");
1685                                 break;
1686                         }
1687                         ManipulatorTransform();
1688                 }
1689                 else if (drawflags == MAN_ROT_T) { /* trackbal need special case, init is different */
1690                         initManipulator(TFM_TRACKBALL);
1691                         ManipulatorTransform();
1692                 }
1693                 else if (drawflags & MAN_ROT_C) {
1694                         initManipulator(TFM_ROTATION);
1695                         switch(drawflags) {
1696                         case MAN_ROT_X:
1697                                 BIF_setSingleAxisConstraint(v3d->twmat[0], " X");
1698                                 break;
1699                         case MAN_ROT_Y:
1700                                 BIF_setSingleAxisConstraint(v3d->twmat[1], " Y");
1701                                 break;
1702                         case MAN_ROT_Z:
1703                                 BIF_setSingleAxisConstraint(v3d->twmat[2], " Z");
1704                                 break;
1705                         }
1706                         ManipulatorTransform();
1707                 }
1708         }
1709         /* after transform, restore drawflags */
1710         drawflags= 0xFFFF;
1711         
1712         return val;
1713 }
1714
1715