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