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