diff options
author | Martin Poirier <theeth@yahoo.com> | 2008-12-29 04:41:28 +0300 |
---|---|---|
committer | Martin Poirier <theeth@yahoo.com> | 2008-12-29 04:41:28 +0300 |
commit | b6b61681efd322bd800bcaba55099cdf147547b2 (patch) | |
tree | bf745c0c58ee608ac8c3c53aa97a14932fc9135f /source/blender/editors/transform/transform_manipulator.c | |
parent | 97a82102d4d24a94d8360a6f5f054e44f6fb1993 (diff) |
2.5
Transform:
First working port of the transform code:
- Object mode only (other conversions need to be ported)
- Contraints (global and local only) working
- Snap (no edit mode, obviously) working
- Numinput working
- Gears (Ctrl and Shift) working
- Only grap, rotate, scale, shear, warp and to sphere have been added as hotkey, but the rest should work too once accessible
- No manipulator
- No drawn feedback other than moving stuff and header print (no constraint line, snap circle, ...)
- No NDOF support
I've only tested Scons support, though Makefil *should* work, I *think*.
Misc:
-QuatIsNull function in arith
-Exporting project_* and view[line|ray] functions from view3d
Diffstat (limited to 'source/blender/editors/transform/transform_manipulator.c')
-rw-r--r-- | source/blender/editors/transform/transform_manipulator.c | 1683 |
1 files changed, 1683 insertions, 0 deletions
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c new file mode 100644 index 00000000000..31d90fba099 --- /dev/null +++ b/source/blender/editors/transform/transform_manipulator.c @@ -0,0 +1,1683 @@ +/** +* $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +// TRANSFORM_FIX_ME +// Disable everything here, don't need it for now +#if 0 + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_armature_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_armature.h" +#include "BKE_global.h" +#include "BKE_lattice.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +#include "BIF_editarmature.h" +#include "BIF_gl.h" +#include "BIF_mywindow.h" +#include "BIF_resources.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_transform.h" +#include "BIF_editmesh.h" +#include "BIF_editparticle.h" + +#include "BSE_edit.h" +#include "BSE_view.h" +#include "BDR_drawobject.h" + +#include "blendef.h" +#include "transform.h" + +/* return codes for select, and drawing flags */ + +#define MAN_TRANS_X 1 +#define MAN_TRANS_Y 2 +#define MAN_TRANS_Z 4 +#define MAN_TRANS_C 7 + +#define MAN_ROT_X 8 +#define MAN_ROT_Y 16 +#define MAN_ROT_Z 32 +#define MAN_ROT_V 64 +#define MAN_ROT_T 128 +#define MAN_ROT_C 248 + +#define MAN_SCALE_X 256 +#define MAN_SCALE_Y 512 +#define MAN_SCALE_Z 1024 +#define MAN_SCALE_C 1792 + +/* color codes */ + +#define MAN_RGB 0 +#define MAN_GHOST 1 +#define MAN_MOVECOL 2 + +/* GLOBAL VARIABLE THAT SHOULD MOVED TO SCREEN MEMBER OR SOMETHING */ +extern TransInfo Trans; + + +static int is_mat4_flipped(float mat[][4]) +{ + float vec[3]; + + Crossf(vec, mat[0], mat[1]); + if( Inpf(vec, mat[2]) < 0.0 ) return 1; + return 0; +} + +/* transform widget center calc helper for below */ +static void calc_tw_center(float *co) +{ + float *twcent= G.scene->twcent; + float *min= G.scene->twmin; + float *max= G.scene->twmax; + + DO_MINMAX(co, min, max); + VecAddf(twcent, twcent, co); +} + +static void protectflag_to_drawflags(short protectflag, short *drawflags) +{ + if(protectflag & OB_LOCK_LOCX) + *drawflags &= ~MAN_TRANS_X; + if(protectflag & OB_LOCK_LOCY) + *drawflags &= ~MAN_TRANS_Y; + if(protectflag & OB_LOCK_LOCZ) + *drawflags &= ~MAN_TRANS_Z; + + if(protectflag & OB_LOCK_ROTX) + *drawflags &= ~MAN_ROT_X; + if(protectflag & OB_LOCK_ROTY) + *drawflags &= ~MAN_ROT_Y; + if(protectflag & OB_LOCK_ROTZ) + *drawflags &= ~MAN_ROT_Z; + + if(protectflag & OB_LOCK_SCALEX) + *drawflags &= ~MAN_SCALE_X; + if(protectflag & OB_LOCK_SCALEY) + *drawflags &= ~MAN_SCALE_Y; + if(protectflag & OB_LOCK_SCALEZ) + *drawflags &= ~MAN_SCALE_Z; +} + +/* for pose mode */ +static void stats_pose(View3D *v3d, bPoseChannel *pchan) +{ + Bone *bone= pchan->bone; + + if(bone) { + if (bone->flag & BONE_TRANSFORM) { + calc_tw_center(pchan->pose_head); + protectflag_to_drawflags(pchan->protectflag, &v3d->twdrawflag); + } + } +} + +/* for editmode*/ +static void stats_editbone(View3D *v3d, EditBone *ebo) +{ + if (ebo->flag & BONE_EDITMODE_LOCKED) + protectflag_to_drawflags(OB_LOCK_LOC|OB_LOCK_ROT|OB_LOCK_SCALE, &v3d->twdrawflag); +} + +/* only counts the parent selection, and tags transform flag */ +/* bad call... should re-use method from transform_conversion once */ +static void count_bone_select(TransInfo *t, bArmature *arm, ListBase *lb, int do_it) +{ + Bone *bone; + int do_next; + + for(bone= lb->first; bone; bone= bone->next) { + bone->flag &= ~BONE_TRANSFORM; + do_next= do_it; + if(do_it) { + if(bone->layer & arm->layer) { + if (bone->flag & BONE_SELECTED) { + /* We don't let connected children get "grabbed" */ + if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_CONNECTED)==0 ) { + bone->flag |= BONE_TRANSFORM; + t->total++; + do_next= 0; // no transform on children if one parent bone is selected + } + } + } + } + count_bone_select(t, arm, &bone->childbase, do_next); + } +} + +/* centroid, boundbox, of selection */ +/* returns total items selected */ +int calc_manipulator_stats(ScrArea *sa) +{ + extern ListBase editNurb; + TransInfo *t; + View3D *v3d= sa->spacedata.first; + Base *base; + Object *ob= OBACT; + float normal[3]={0.0, 0.0, 0.0}; + float plane[3]={0.0, 0.0, 0.0}; + int a, totsel=0; + + t = BIF_GetTransInfo(); + + /* transform widget matrix */ + Mat4One(v3d->twmat); + + v3d->twdrawflag= 0xFFFF; + + /* transform widget centroid/center */ + G.scene->twcent[0]= G.scene->twcent[1]= G.scene->twcent[2]= 0.0f; + INIT_MINMAX(G.scene->twmin, G.scene->twmax); + + if(G.obedit) { + ob= G.obedit; + if((ob->lay & G.vd->lay)==0) return 0; + + if(G.obedit->type==OB_MESH) { + EditMesh *em = G.editMesh; + EditVert *eve; + EditSelection ese; + float vec[3]= {0,0,0}; + + /* USE LAST SELECTE WITH ACTIVE */ + if (G.vd->around==V3D_ACTIVE && EM_get_actSelection(&ese)) { + EM_editselection_center(vec, &ese); + calc_tw_center(vec); + totsel= 1; + } else { + /* do vertices for center, and if still no normal found, use vertex normals */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + totsel++; + calc_tw_center(eve->co); + } + } + } + } /* end editmesh */ + else if (G.obedit->type==OB_ARMATURE){ + bArmature *arm= G.obedit->data; + EditBone *ebo; + for (ebo=G.edbo.first;ebo;ebo=ebo->next){ + if(ebo->layer & arm->layer) { + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(ebo->tail); + totsel++; + } + if (ebo->flag & BONE_ROOTSEL) { + calc_tw_center(ebo->head); + totsel++; + } + if (ebo->flag & BONE_SELECTED) { + stats_editbone(v3d, ebo); + } + } + } + } + else if ELEM3(G.obedit->type, OB_CURVE, OB_SURF, OB_FONT) { + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + + nu= editNurb.first; + while(nu) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + /* exceptions + * if handles are hidden then only check the center points. + * If 2 or more are selected then only use the center point too. + */ + if (G.f & G_HIDDENHANDLES) { + if (bezt->f2 & SELECT) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + } + else if ( (bezt->f1 & SELECT) + (bezt->f2 & SELECT) + (bezt->f3 & SELECT) > SELECT ) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + else { + if(bezt->f1) { + calc_tw_center(bezt->vec[0]); + totsel++; + } + if(bezt->f2) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + if(bezt->f3) { + calc_tw_center(bezt->vec[2]); + totsel++; + } + } + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(bp->f1 & SELECT) { + calc_tw_center(bp->vec); + totsel++; + } + bp++; + } + } + nu= nu->next; + } + } + else if(G.obedit->type==OB_MBALL) { + /* editmball.c */ + extern ListBase editelems; /* go away ! */ + MetaElem *ml, *ml_sel=NULL; + + ml= editelems.first; + while(ml) { + if(ml->flag & SELECT) { + calc_tw_center(&ml->x); + ml_sel = ml; + totsel++; + } + ml= ml->next; + } + } + else if(G.obedit->type==OB_LATTICE) { + BPoint *bp; + bp= editLatt->def; + + a= editLatt->pntsu*editLatt->pntsv*editLatt->pntsw; + while(a--) { + if(bp->f1 & SELECT) { + calc_tw_center(bp->vec); + totsel++; + } + bp++; + } + } + + /* selection center */ + if(totsel) { + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + Mat4MulVecfl(G.obedit->obmat, G.scene->twcent); + Mat4MulVecfl(G.obedit->obmat, G.scene->twmin); + Mat4MulVecfl(G.obedit->obmat, G.scene->twmax); + } + } + else if(ob && (ob->flag & OB_POSEMODE)) { + bArmature *arm = ob->data; + bPoseChannel *pchan; + int mode; + + if((ob->lay & G.vd->lay)==0) return 0; + + mode = Trans.mode; + Trans.mode = TFM_ROTATION; // mislead counting bones... bah + + /* count total, we use same method as transform will do */ + Trans.total= 0; + count_bone_select(&Trans, arm, &arm->bonebase, 1); + totsel = Trans.total; + if(totsel) { + /* use channels to get stats */ + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + stats_pose(v3d, pchan); + } + + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + Mat4MulVecfl(ob->obmat, G.scene->twcent); + Mat4MulVecfl(ob->obmat, G.scene->twmin); + Mat4MulVecfl(ob->obmat, G.scene->twmax); + } + /* restore, mode can be TFM_INIT */ + Trans.mode = mode; + } + else if(G.f & (G_VERTEXPAINT + G_TEXTUREPAINT + G_WEIGHTPAINT + G_SCULPTMODE)) { + ; + } + else if(G.f & G_PARTICLEEDIT) { + ParticleSystem *psys=PE_get_current(OBACT); + ParticleData *pa = psys->particles; + ParticleEditKey *ek; + int k; + + if(psys->edit){ + for(a=0; a<psys->totpart; a++,pa++){ + if(pa->flag & PARS_HIDE) continue; + for(k=0, ek=psys->edit->keys[a]; k<pa->totkey; k++,ek++){ + if(ek->flag & PEK_SELECT){ + calc_tw_center(ek->world_co); + totsel++; + } + } + } + /* selection center */ + if(totsel) + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + } + } + else { + + /* we need the one selected object, if its not active */ + ob= OBACT; + if(ob && !(ob->flag & SELECT)) ob= NULL; + + for(base= G.scene->base.first; base; base= base->next) { + if TESTBASELIB(base) { + if(ob==NULL) + ob= base->object; + calc_tw_center(base->object->obmat[3]); + protectflag_to_drawflags(base->object->protectflag, &v3d->twdrawflag); + totsel++; + } + } + + /* selection center */ + if(totsel) { + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + } + } + + /* global, local or normal orientation? */ + if(ob && totsel) { + + switch(v3d->twmode) { + case V3D_MANIP_GLOBAL: + strcpy(t->spacename, "global"); + break; + + case V3D_MANIP_NORMAL: + if(G.obedit || ob->flag & OB_POSEMODE) { + float mat[3][3]; + int type; + + strcpy(t->spacename, "normal"); + + type = getTransformOrientation(normal, plane, (G.vd->around == V3D_ACTIVE)); + + switch (type) + { + case ORIENTATION_NORMAL: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_VERT: + if (createSpaceNormal(mat, normal) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_EDGE: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_FACE: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + } + + if (type == ORIENTATION_NONE) + { + Mat4One(v3d->twmat); + } + else + { + Mat4CpyMat3(v3d->twmat, mat); + } + break; + } + /* no break we define 'normal' as 'local' in Object mode */ + case V3D_MANIP_LOCAL: + strcpy(t->spacename, "local"); + Mat4CpyMat4(v3d->twmat, ob->obmat); + Mat4Ortho(v3d->twmat); + break; + + case V3D_MANIP_VIEW: + { + float mat[3][3]; + strcpy(t->spacename, "view"); + Mat3CpyMat4(mat, v3d->viewinv); + Mat3Ortho(mat); + Mat4CpyMat3(v3d->twmat, mat); + } + break; + default: /* V3D_MANIP_CUSTOM */ + applyTransformOrientation(); + break; + } + + } + + return totsel; +} + +/* ******************** DRAWING STUFFIES *********** */ + +static float screen_aligned(float mat[][4]) +{ + float vec[3], size; + + VECCOPY(vec, mat[0]); + size= Normalize(vec); + + glTranslatef(mat[3][0], mat[3][1], mat[3][2]); + + /* sets view screen aligned */ + glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]); + + return size; +} + + +/* radring = radius of donut rings + radhole = radius hole + start = starting segment (based on nrings) + end = end segment + nsides = amount of points in ring + nrigns = amount of rings +*/ +static void partial_donut(float radring, float radhole, int start, int end, int nsides, int nrings) +{ + float theta, phi, theta1; + float cos_theta, sin_theta; + float cos_theta1, sin_theta1; + float ring_delta, side_delta; + int i, j, docaps= 1; + + if(start==0 && end==nrings) docaps= 0; + + ring_delta= 2.0f*(float)M_PI/(float)nrings; + side_delta= 2.0f*(float)M_PI/(float)nsides; + + theta= (float)M_PI+0.5f*ring_delta; + cos_theta= (float)cos(theta); + sin_theta= (float)sin(theta); + + for(i= nrings - 1; i >= 0; i--) { + theta1= theta + ring_delta; + cos_theta1= (float)cos(theta1); + sin_theta1= (float)sin(theta1); + + if(docaps && i==start) { // cap + glBegin(GL_POLYGON); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + } + glEnd(); + } + if(i>=start && i<=end) { + glBegin(GL_QUAD_STRIP); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + if(docaps && i==end) { // cap + glBegin(GL_POLYGON); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi -= side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + + theta= theta1; + cos_theta= cos_theta1; + sin_theta= sin_theta1; + } +} + +/* three colors can be set; + grey for ghosting + moving: in transform theme color + else the red/green/blue +*/ +static void manipulator_setcolor(char axis, int colcode) +{ + float vec[4]; + char col[4]; + + vec[3]= 0.7f; // alpha set on 0.5, can be glEnabled or not + + if(colcode==MAN_GHOST) { + glColor4ub(0, 0, 0, 70); + } + else if(colcode==MAN_MOVECOL) { + BIF_GetThemeColor3ubv(TH_TRANSFORM, col); + glColor4ub(col[0], col[1], col[2], 128); + } + else { + switch(axis) { + case 'c': + BIF_GetThemeColor3ubv(TH_TRANSFORM, col); + if(G.vd->twmode == V3D_MANIP_LOCAL) { + col[0]= col[0]>200?255:col[0]+55; + col[1]= col[1]>200?255:col[1]+55; + col[2]= col[2]>200?255:col[2]+55; + } + else if(G.vd->twmode == V3D_MANIP_NORMAL) { + col[0]= col[0]<55?0:col[0]-55; + col[1]= col[1]<55?0:col[1]-55; + col[2]= col[2]<55?0:col[2]-55; + } + glColor4ub(col[0], col[1], col[2], 128); + break; + case 'x': + glColor4ub(220, 0, 0, 128); + break; + case 'y': + glColor4ub(0, 220, 0, 128); + break; + case 'z': + glColor4ub(30, 30, 220, 128); + break; + } + } +} + +/* viewmatrix should have been set OK, also no shademode! */ +static void draw_manipulator_axes(int colcode, int flagx, int flagy, int flagz) +{ + + /* axes */ + if(flagx) { + manipulator_setcolor('x', colcode); + if(flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X); + else if(flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X); + glBegin(GL_LINES); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glEnd(); + } + if(flagy) { + if(flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y); + else if(flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y); + manipulator_setcolor('y', colcode); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glEnd(); + } + if(flagz) { + if(flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z); + else if(flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z); + manipulator_setcolor('z', colcode); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + } +} + +/* only called while G.moving */ +static void draw_manipulator_rotate_ghost(float mat[][4], int drawflags) +{ + GLUquadricObj *qobj; + float size, phi, startphi, vec[3], svec[3], matt[4][4], cross[3], tmat[3][3]; + int arcs= (G.rt!=2); + + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + glColor4ub(0,0,0,64); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* we need both [4][4] transforms, Trans.mat seems to be premul, not post for mat[][4] */ + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + + /* prepare for screen aligned draw */ + glPushMatrix(); + size= screen_aligned(mat); + + vec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + + startphi= saacos( vec[1] ); + if(vec[0]<0.0) startphi= -startphi; + + phi= (float)fmod(180.0*Trans.val/M_PI, 360.0); + if(phi > 180.0) phi-= 360.0; + else if(phi<-180.0) phi+= 360.0; + + gluPartialDisk(qobj, 0.0, size, 32, 1, 180.0*startphi/M_PI, phi); + + glPopMatrix(); + } + else if(arcs) { + float imat[3][3], ivmat[3][3]; + /* try to get the start rotation */ + + svec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]); + svec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]); + svec[2]= 0.0f; + + /* screen aligned vec transform back to manipulator space */ + Mat3CpyMat4(ivmat, G.vd->viewinv); + Mat3CpyMat4(tmat, mat); + Mat3Inv(imat, tmat); + Mat3MulMat3(tmat, imat, ivmat); + + Mat3MulVecfl(tmat, svec); // tmat is used further on + Normalize(svec); + } + + mymultmatrix(mat); // aligns with original widget + + /* Z disk */ + if(drawflags & MAN_ROT_Z) { + if(arcs) { + /* correct for squeezed arc */ + svec[0]+= tmat[2][0]; + svec[1]+= tmat[2][1]; + Normalize(svec); + + startphi= (float)atan2(svec[0], svec[1]); + } + else startphi= 0.5f*(float)M_PI; + + VECCOPY(vec, mat[0]); // use x axis to detect rotation + Normalize(vec); + Normalize(matt[0]); + phi= saacos( Inpf(vec, matt[0]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[0]); // results in z vector + if(Inpf(cross, mat[2]) > 0.0) phi= -phi; + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*(phi)/M_PI); + } + } + /* X disk */ + if(drawflags & MAN_ROT_X) { + if(arcs) { + /* correct for squeezed arc */ + svec[1]+= tmat[2][1]; + svec[2]+= tmat[2][2]; + Normalize(svec); + + startphi= (float)(M_PI + atan2(svec[2], -svec[1])); + } + else startphi= 0.0f; + + VECCOPY(vec, mat[1]); // use y axis to detect rotation + Normalize(vec); + Normalize(matt[1]); + phi= saacos( Inpf(vec, matt[1]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[1]); // results in x vector + if(Inpf(cross, mat[0]) > 0.0) phi= -phi; + glRotatef(90.0, 0.0, 1.0, 0.0); + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(arcs) { + /* correct for squeezed arc */ + svec[0]+= tmat[2][0]; + svec[2]+= tmat[2][2]; + Normalize(svec); + + startphi= (float)(M_PI + atan2(-svec[0], svec[2])); + } + else startphi= (float)M_PI; + + VECCOPY(vec, mat[2]); // use z axis to detect rotation + Normalize(vec); + Normalize(matt[2]); + phi= saacos( Inpf(vec, matt[2]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[2]); // results in y vector + if(Inpf(cross, mat[1]) > 0.0) phi= -phi; + glRotatef(-90.0, 1.0, 0.0, 0.0); + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + } + + glDisable(GL_BLEND); + myloadmatrix(G.vd->viewmat); +} + +static void draw_manipulator_rotate(float mat[][4], int moving, int drawflags, int combo) +{ + GLUquadricObj *qobj; + double plane[4]; + float size, vec[3], unitmat[4][4]; + float cywid= 0.33f*0.01f*(float)U.tw_handlesize; + float cusize= cywid*0.65f; + int arcs= (G.rt!=2); + int colcode; + + if(moving) colcode= MAN_MOVECOL; + else colcode= MAN_RGB; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_ROT_C)==0) return; + + /* Init stuff */ + glDisable(GL_DEPTH_TEST); + Mat4One(unitmat); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + /* prepare for screen aligned draw */ + VECCOPY(vec, mat[0]); + size= Normalize(vec); + glPushMatrix(); + glTranslatef(mat[3][0], mat[3][1], mat[3][2]); + + if(arcs) { + /* clipplane makes nice handles, calc here because of multmatrix but with translate! */ + VECCOPY(plane, G.vd->viewinv[2]); + plane[3]= -0.02*size; // clip just a bit more + glClipPlane(GL_CLIP_PLANE0, plane); + } + /* sets view screen aligned */ + glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]); + + /* Screen aligned help circle */ + if(arcs) { + if((G.f & G_PICKSEL)==0) { + BIF_ThemeColorShade(TH_BACK, -30); + drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat); + } + } + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V); + BIF_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat); + + if(moving) { + float vec[3]; + vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + VecMulf(vec, 1.2f*size); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + /* apply the transform delta */ + if(moving) { + float matt[4][4]; + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + mymultmatrix(matt); + glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW); + } + else { + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + mymultmatrix(mat); + } + + /* axes */ + if(arcs==0) { + if(!(G.f & G_PICKSEL)) { + if( (combo & V3D_MANIP_SCALE)==0) { + /* axis */ + glBegin(GL_LINES); + if( (drawflags & MAN_ROT_X) || (moving && (drawflags & MAN_ROT_Z)) ) { + manipulator_setcolor('x', colcode); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + } + if( (drawflags & MAN_ROT_Y) || (moving && (drawflags & MAN_ROT_X)) ) { + manipulator_setcolor('y', colcode); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + } + if( (drawflags & MAN_ROT_Z) || (moving && (drawflags & MAN_ROT_Y)) ) { + manipulator_setcolor('z', colcode); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + } + glEnd(); + } + } + } + + if(arcs==0 && moving) { + + /* Z circle */ + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + } + /* X circle */ + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + + if(arcs) glDisable(GL_CLIP_PLANE0); + } + // donut arcs + if(arcs) { + glEnable(GL_CLIP_PLANE0); + + /* Z circle */ + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + } + /* X circle */ + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + + glDisable(GL_CLIP_PLANE0); + } + + if(arcs==0) { + + /* Z handle on X axis */ + if(drawflags & MAN_ROT_Z) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + /* Y handle on X axis */ + if(drawflags & MAN_ROT_Y) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + manipulator_setcolor('y', colcode); + + glRotatef(90.0, 1.0, 0.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + /* X handle on Z axis */ + if(drawflags & MAN_ROT_X) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + manipulator_setcolor('x', colcode); + + glRotatef(-90.0, 0.0, 1.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + } + + /* restore */ + myloadmatrix(G.vd->viewmat); + gluDeleteQuadric(qobj); + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void draw_manipulator_scale(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + float cywid= 0.25f*0.01f*(float)U.tw_handlesize; + float cusize= cywid*0.75f, dz; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_SCALE_C)==0) return; + + glDisable(GL_DEPTH_TEST); + + /* not in combo mode */ + if( (combo & (V3D_MANIP_TRANSLATE|V3D_MANIP_ROTATE))==0) { + float size, unitmat[4][4]; + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_SCALE_C); + + manipulator_setcolor('c', colcode); + glPushMatrix(); + size= screen_aligned(mat); + Mat4One(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat); + glPopMatrix(); + + dz= 1.0; + } + else dz= 1.0f-4.0f*cusize; + + if(moving) { + float matt[4][4]; + + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + mymultmatrix(matt); + glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW); + } + else { + mymultmatrix(mat); + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + } + + /* axis */ + + /* in combo mode, this is always drawn as first type */ + draw_manipulator_axes(colcode, drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z); + + /* Z cube */ + glTranslatef(0.0, 0.0, dz); + if(drawflags & MAN_SCALE_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z); + manipulator_setcolor('z', colcode); + drawsolidcube(cusize); + } + /* X cube */ + glTranslatef(dz, 0.0, -dz); + if(drawflags & MAN_SCALE_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_X); + manipulator_setcolor('x', colcode); + drawsolidcube(cusize); + } + /* Y cube */ + glTranslatef(-dz, dz, 0.0); + if(drawflags & MAN_SCALE_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y); + manipulator_setcolor('y', colcode); + drawsolidcube(cusize); + } + + /* if shiftkey, center point as last, for selectbuffer order */ + if(G.f & G_PICKSEL) { + if(G.qual & LR_SHIFTKEY) { + glTranslatef(0.0, -dz, 0.0); + glLoadName(MAN_SCALE_C); + glBegin(GL_POINTS); + glVertex3f(0.0, 0.0, 0.0); + glEnd(); + } + } + + /* restore */ + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + glFrontFace(GL_CCW); +} + + +static void draw_cone(GLUquadricObj *qobj, float len, float width) +{ + glTranslatef(0.0, 0.0, -0.5f*len); + gluCylinder(qobj, width, 0.0, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, 0.5f*len); +} + +static void draw_cylinder(GLUquadricObj *qobj, float len, float width) +{ + + width*= 0.8f; // just for beauty + + glTranslatef(0.0, 0.0, -0.5f*len); + gluCylinder(qobj, width, width, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, len); + gluDisk(qobj, 0.0, width, 8, 1); + glTranslatef(0.0, 0.0, -0.5f*len); +} + + +static void draw_manipulator_translate(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + GLUquadricObj *qobj; + float cylen= 0.01f*(float)U.tw_handlesize; + float cywid= 0.25f*cylen, dz, size; + float unitmat[4][4]; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_TRANS_C)==0) return; + + if(moving) glTranslatef(Trans.vec[0], Trans.vec[1], Trans.vec[2]); + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_TRANS_C); + + manipulator_setcolor('c', colcode); + glPushMatrix(); + size= screen_aligned(mat); + Mat4One(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat); + glPopMatrix(); + + /* and now apply matrix, we move to local matrix drawing */ + mymultmatrix(mat); + + /* axis */ + glLoadName(-1); + + // translate drawn as last, only axis when no combo with scale, or for ghosting + if((combo & V3D_MANIP_SCALE)==0 || colcode==MAN_GHOST) + draw_manipulator_axes(colcode, drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z); + + + /* offset in combo mode, for rotate a bit more */ + if(combo & (V3D_MANIP_ROTATE)) dz= 1.0f+2.0f*cylen; + else if(combo & (V3D_MANIP_SCALE)) dz= 1.0f+0.5f*cylen; + else dz= 1.0f; + + /* Z Cone */ + glTranslatef(0.0, 0.0, dz); + if(drawflags & MAN_TRANS_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z); + manipulator_setcolor('z', colcode); + draw_cone(qobj, cylen, cywid); + } + /* X Cone */ + glTranslatef(dz, 0.0, -dz); + if(drawflags & MAN_TRANS_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + draw_cone(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y Cone */ + glTranslatef(-dz, dz, 0.0); + if(drawflags & MAN_TRANS_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + draw_cone(qobj, cylen, cywid); + } + + gluDeleteQuadric(qobj); + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void draw_manipulator_rotate_cyl(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + GLUquadricObj *qobj; + float size; + float cylen= 0.01f*(float)U.tw_handlesize; + float cywid= 0.25f*cylen; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_ROT_C)==0) return; + + /* prepare for screen aligned draw */ + glPushMatrix(); + size= screen_aligned(mat); + + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + float unitmat[4][4]; + Mat4One(unitmat); + + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V); + BIF_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat); + + if(moving) { + float vec[3]; + vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + VecMulf(vec, 1.2f*size); + glBegin(GL_LINES); + glVertex3f(0.0, 0.0, 0.0); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + /* apply the transform delta */ + if(moving) { + float matt[4][4]; + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + if (Trans.flag & T_USES_MANIPULATOR) { + Mat4MulMat34(matt, Trans.mat, mat); + } + mymultmatrix(matt); + } + else { + mymultmatrix(mat); + } + + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + + /* axis */ + if( (G.f & G_PICKSEL)==0 ) { + + // only draw axis when combo didn't draw scale axes + if((combo & V3D_MANIP_SCALE)==0) + draw_manipulator_axes(colcode, drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z); + + /* only has to be set when not in picking */ + gluQuadricDrawStyle(qobj, GLU_FILL); + } + + /* Z cyl */ + glTranslatef(0.0, 0.0, 1.0); + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + draw_cylinder(qobj, cylen, cywid); + } + /* X cyl */ + glTranslatef(1.0, 0.0, -1.0); + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + draw_cylinder(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y cylinder */ + glTranslatef(-1.0, 1.0, 0.0); + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + draw_cylinder(qobj, cylen, cywid); + } + + /* restore */ + + gluDeleteQuadric(qobj); + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + + +/* ********************************************* */ + +static float get_manipulator_drawsize(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + float size = get_drawsize(v3d, v3d->twmat[3]); + + size*= (float)U.tw_size; + + return size; +} + +/* exported to transform_constraints.c */ +/* mat, vec = default orientation and location */ +/* type = transform type */ +/* axis = x, y, z, c */ +/* col: 0 = colored, 1 = moving, 2 = ghost */ +void draw_manipulator_ext(ScrArea *sa, int type, char axis, int col, float vec[3], float mat[][3]) +{ + int drawflags= 0; + float mat4[4][4]; + int colcode; + + Mat4CpyMat3(mat4, mat); + VECCOPY(mat4[3], vec); + + Mat4MulFloat3((float *)mat4, get_manipulator_drawsize(sa)); + + glEnable(GL_BLEND); // let's do it transparent by default + if(col==0) colcode= MAN_RGB; + else if(col==1) colcode= MAN_MOVECOL; + else colcode= MAN_GHOST; + + + if(type==TFM_ROTATION) { + if(axis=='x') drawflags= MAN_ROT_X; + else if(axis=='y') drawflags= MAN_ROT_Y; + else if(axis=='z') drawflags= MAN_ROT_Z; + else drawflags= MAN_ROT_C; + + draw_manipulator_rotate_cyl(mat4, col, drawflags, V3D_MANIP_ROTATE, colcode); + } + else if(type==TFM_RESIZE) { + if(axis=='x') drawflags= MAN_SCALE_X; + else if(axis=='y') drawflags= MAN_SCALE_Y; + else if(axis=='z') drawflags= MAN_SCALE_Z; + else drawflags= MAN_SCALE_C; + + draw_manipulator_scale(mat4, col, drawflags, V3D_MANIP_SCALE, colcode); + } + else { + if(axis=='x') drawflags= MAN_TRANS_X; + else if(axis=='y') drawflags= MAN_TRANS_Y; + else if(axis=='z') drawflags= MAN_TRANS_Z; + else drawflags= MAN_TRANS_C; + + draw_manipulator_translate(mat4, 0, drawflags, V3D_MANIP_TRANSLATE, colcode); + } + + + glDisable(GL_BLEND); +} + +/* main call, does calc centers & orientation too */ +/* uses global G.moving */ +static int drawflags= 0xFFFF; // only for the calls below, belongs in scene...? +void BIF_draw_manipulator(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + int totsel; + + if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return; + if(G.moving && (G.moving & G_TRANSFORM_MANIP)==0) return; + + if(G.moving==0) { + v3d->twflag &= ~V3D_DRAW_MANIPULATOR; + + totsel= calc_manipulator_stats(sa); + if(totsel==0) return; + drawflags= v3d->twdrawflag; /* set in calc_manipulator_stats */ + + v3d->twflag |= V3D_DRAW_MANIPULATOR; + + /* now we can define center */ + switch(v3d->around) { + case V3D_CENTER: + case V3D_ACTIVE: + v3d->twmat[3][0]= (G.scene->twmin[0] + G.scene->twmax[0])/2.0f; + v3d->twmat[3][1]= (G.scene->twmin[1] + G.scene->twmax[1])/2.0f; + v3d->twmat[3][2]= (G.scene->twmin[2] + G.scene->twmax[2])/2.0f; + if(v3d->around==V3D_ACTIVE && G.obedit==NULL) { + Object *ob= OBACT; + if(ob && !(ob->flag & OB_POSEMODE)) + VECCOPY(v3d->twmat[3], ob->obmat[3]); + } + break; + case V3D_LOCAL: + case V3D_CENTROID: + VECCOPY(v3d->twmat[3], G.scene->twcent); + break; + case V3D_CURSOR: + VECCOPY(v3d->twmat[3], give_cursor()); + break; + } + + Mat4MulFloat3((float *)v3d->twmat, get_manipulator_drawsize(sa)); + } + + if(v3d->twflag & V3D_DRAW_MANIPULATOR) { + + if(v3d->twtype & V3D_MANIP_ROTATE) { + + /* rotate has special ghosting draw, for pie chart */ + if(G.moving) draw_manipulator_rotate_ghost(v3d->twmat, drawflags); + + if(G.moving) glEnable(GL_BLEND); + + if(G.rt==3) { + if(G.moving) draw_manipulator_rotate_cyl(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + else draw_manipulator_rotate_cyl(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + else + draw_manipulator_rotate(v3d->twmat, G.moving, drawflags, v3d->twtype); + + glDisable(GL_BLEND); + } + if(v3d->twtype & V3D_MANIP_SCALE) { + if(G.moving) { + glEnable(GL_BLEND); + draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST); + draw_manipulator_scale(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + glDisable(GL_BLEND); + } + else draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + if(v3d->twtype & V3D_MANIP_TRANSLATE) { + if(G.moving) { + glEnable(GL_BLEND); + draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST); + draw_manipulator_translate(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + glDisable(GL_BLEND); + } + else draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + } +} + +static int manipulator_selectbuf(ScrArea *sa, float hotspot) +{ + View3D *v3d= sa->spacedata.first; + rctf rect; + GLuint buffer[64]; // max 4 items per select, so large enuf + short hits, mval[2]; + + G.f |= G_PICKSEL; + + getmouseco_areawin(mval); + rect.xmin= mval[0]-hotspot; + rect.xmax= mval[0]+hotspot; + rect.ymin= mval[1]-hotspot; + rect.ymax= mval[1]+hotspot; + + /* get rid of overlay button matrix */ + persp(PERSP_VIEW); + + setwinmatrixview3d(sa->winx, sa->winy, &rect); + Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat); + + glSelectBuffer( 64, buffer); + glRenderMode(GL_SELECT); + glInitNames(); /* these two calls whatfor? It doesnt work otherwise */ + glPushName(-2); + + /* do the drawing */ + if(v3d->twtype & V3D_MANIP_ROTATE) { + if(G.rt==3) draw_manipulator_rotate_cyl(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + else draw_manipulator_rotate(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype); + } + if(v3d->twtype & V3D_MANIP_SCALE) + draw_manipulator_scale(v3d->twmat, 0, MAN_SCALE_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + if(v3d->twtype & V3D_MANIP_TRANSLATE) + draw_manipulator_translate(v3d->twmat, 0, MAN_TRANS_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + + glPopName(); + hits= glRenderMode(GL_RENDER); + + G.f &= ~G_PICKSEL; + setwinmatrixview3d(sa->winx, sa->winy, NULL); + Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat); + + persp(PERSP_WIN); + + if(hits==1) return buffer[3]; + else if(hits>1) { + GLuint val, dep, mindep=0, mindeprot=0, minval=0, minvalrot=0; + int a; + + /* we compare the hits in buffer, but value centers highest */ + /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */ + + for(a=0; a<hits; a++) { + dep= buffer[4*a + 1]; + val= buffer[4*a + 3]; + + if(val==MAN_TRANS_C) return MAN_TRANS_C; + else if(val==MAN_SCALE_C) return MAN_SCALE_C; + else { + if(val & MAN_ROT_C) { + if(minvalrot==0 || dep<mindeprot) { + mindeprot= dep; + minvalrot= val; + } + } + else { + if(minval==0 || dep<mindep) { + mindep= dep; + minval= val; + } + } + } + } + + if(minval) + return minval; + else + return minvalrot; + } + return 0; +} + +/* return 0; nothing happened */ +int BIF_do_manipulator(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + int val; + + if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return 0; + if(!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return 0; + + // find the hotspots first test narrow hotspot + val= manipulator_selectbuf(sa, 0.5f*(float)U.tw_hotspot); + if(val) { + checkFirstTime(); // TEMPORARY, check this before doing any transform call. + // drawflags still global, for drawing call above + drawflags= manipulator_selectbuf(sa, 0.2f*(float)U.tw_hotspot); + if(drawflags==0) drawflags= val; + + if (drawflags & MAN_TRANS_C) { + initManipulator(TFM_TRANSLATION); + switch(drawflags) { + case MAN_TRANS_C: + break; + case MAN_TRANS_X: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_Y|MAN_TRANS_Z; + BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_TRANS_Y: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_X|MAN_TRANS_Z; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_TRANS_Z: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_X|MAN_TRANS_Y; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + else if (drawflags & MAN_SCALE_C) { + initManipulator(TFM_RESIZE); + switch(drawflags) { + case MAN_SCALE_X: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_Y|MAN_SCALE_Z; + BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_SCALE_Y: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_X|MAN_SCALE_Z; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_SCALE_Z: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_X|MAN_SCALE_Y; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + else if (drawflags == MAN_ROT_T) { /* trackbal need special case, init is different */ + initManipulator(TFM_TRACKBALL); + ManipulatorTransform(); + } + else if (drawflags & MAN_ROT_C) { + initManipulator(TFM_ROTATION); + switch(drawflags) { + case MAN_ROT_X: + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_ROT_Y: + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_ROT_Z: + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + } + /* after transform, restore drawflags */ + drawflags= 0xFFFF; + + return val; +} + +#endif |