2.5
[blender.git] / source / blender / editors / space_view3d / view3d_edit.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) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_action_types.h"
35 #include "DNA_armature_types.h"
36 #include "DNA_camera_types.h"
37 #include "DNA_lamp_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_userdef_types.h"
43 #include "DNA_view3d_types.h"
44 #include "DNA_world_types.h"
45
46 #include "MEM_guardedalloc.h"
47
48 #include "BLI_blenlib.h"
49 #include "BLI_arithb.h"
50 #include "BLI_rand.h"
51
52 #include "BKE_action.h"
53 #include "BKE_context.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_object.h"
56 #include "BKE_global.h"
57 #include "BKE_scene.h"
58 #include "BKE_screen.h"
59 #include "BKE_utildefines.h"
60
61 #include "RE_pipeline.h"        // make_stars
62
63 #include "BIF_gl.h"
64 #include "BIF_retopo.h"
65
66 #include "WM_api.h"
67 #include "WM_types.h"
68
69 #include "RNA_access.h"
70 #include "RNA_define.h"
71
72 #include "ED_screen.h"
73 #include "ED_types.h"
74
75 #include "UI_interface.h"
76 #include "UI_resources.h"
77 #include "UI_view2d.h"
78
79 #include "PIL_time.h" /* smoothview */
80
81 #include "view3d_intern.h"      // own include
82
83 /* ********************** view3d_edit: view manipulations ********************* */
84
85 /* ************************** init for view ops **********************************/
86
87 typedef struct ViewOpsData {
88         ARegion *ar;
89         View3D *v3d;
90         
91         float oldquat[4];
92         float trackvec[3];
93         float ofs[3], obofs[3];
94         float reverse, dist0;
95         
96         int origx, origy, oldx, oldy;
97         int origkey;
98         
99 } ViewOpsData;
100
101 #define TRACKBALLSIZE  (1.1)
102
103 static void calctrackballvec(rcti *rect, int mx, int my, float *vec)
104 {
105         float x, y, radius, d, z, t;
106         
107         radius= TRACKBALLSIZE;
108         
109         /* normalize x and y */
110         x= (rect->xmax + rect->xmin)/2 - mx;
111         x/= (float)((rect->xmax - rect->xmin)/4);
112         y= (rect->ymax + rect->ymin)/2 - my;
113         y/= (float)((rect->ymax - rect->ymin)/2);
114         
115         d = sqrt(x*x + y*y);
116         if (d < radius*M_SQRT1_2)       /* Inside sphere */
117                 z = sqrt(radius*radius - d*d);
118         else
119         {                       /* On hyperbola */
120                 t = radius / M_SQRT2;
121                 z = t*t / d;
122         }
123
124         vec[0]= x;
125         vec[1]= y;
126         vec[2]= -z;             /* yah yah! */
127 }
128
129
130 static void viewops_data(bContext *C, wmOperator *op, wmEvent *event)
131 {
132         ScrArea *sa= CTX_wm_area(C);
133         View3D *v3d= sa->spacedata.first;
134         ViewOpsData *vod= MEM_callocN(sizeof(ViewOpsData), "viewops data");
135         
136         /* store data */
137         op->customdata= vod;
138         vod->ar= CTX_wm_region(C);
139         vod->v3d= v3d;
140         vod->dist0= v3d->dist;
141         QUATCOPY(vod->oldquat, v3d->viewquat);
142         vod->origx= vod->oldx= event->x;
143         vod->origy= vod->oldy= event->y;
144         vod->origkey= event->type;
145         
146         calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
147         
148         initgrabz(v3d, -v3d->ofs[0], -v3d->ofs[1], -v3d->ofs[2]);
149         
150         vod->reverse= 1.0f;
151         if (v3d->persmat[2][1] < 0.0f)
152                 vod->reverse= -1.0f;
153         
154 }       
155
156 /* ************************** viewrotate **********************************/
157
158 static const float thres = 0.93f; //cos(20 deg);
159
160 #define COS45 0.70710678118654746
161 #define SIN45 COS45
162
163 static float snapquats[39][6] = {
164         /*{q0, q1, q3, q4, view, oposite_direction}*/
165 {COS45, -SIN45, 0.0, 0.0, 1, 0},  //front
166 {0.0, 0.0, -SIN45, -SIN45, 1, 1}, //back
167 {1.0, 0.0, 0.0, 0.0, 7, 0},       //top
168 {0.0, -1.0, 0.0, 0.0, 7, 1},      //bottom
169 {0.5, -0.5, -0.5, -0.5, 3, 0},    //left
170 {0.5, -0.5, 0.5, 0.5, 3, 1},      //right
171         
172         /* some more 45 deg snaps */
173 {0.65328145027160645, -0.65328145027160645, 0.27059805393218994, 0.27059805393218994, 0, 0},
174 {0.92387950420379639, 0.0, 0.0, 0.38268342614173889, 0, 0},
175 {0.0, -0.92387950420379639, 0.38268342614173889, 0.0, 0, 0},
176 {0.35355335474014282, -0.85355335474014282, 0.35355338454246521, 0.14644660055637360, 0, 0},
177 {0.85355335474014282, -0.35355335474014282, 0.14644660055637360, 0.35355338454246521, 0, 0},
178 {0.49999994039535522, -0.49999994039535522, 0.49999997019767761, 0.49999997019767761, 0, 0},
179 {0.27059802412986755, -0.65328145027160645, 0.65328145027160645, 0.27059802412986755, 0, 0},
180 {0.65328145027160645, -0.27059802412986755, 0.27059802412986755, 0.65328145027160645, 0, 0},
181 {0.27059799432754517, -0.27059799432754517, 0.65328139066696167, 0.65328139066696167, 0, 0},
182 {0.38268336653709412, 0.0, 0.0, 0.92387944459915161, 0, 0},
183 {0.0, -0.38268336653709412, 0.92387944459915161, 0.0, 0, 0},
184 {0.14644658565521240, -0.35355335474014282, 0.85355335474014282, 0.35355335474014282, 0, 0},
185 {0.35355335474014282, -0.14644658565521240, 0.35355335474014282, 0.85355335474014282, 0, 0},
186 {0.0, 0.0, 0.92387944459915161, 0.38268336653709412, 0, 0},
187 {-0.0, 0.0, 0.38268336653709412, 0.92387944459915161, 0, 0},
188 {-0.27059802412986755, 0.27059802412986755, 0.65328133106231689, 0.65328133106231689, 0, 0},
189 {-0.38268339633941650, 0.0, 0.0, 0.92387938499450684, 0, 0},
190 {0.0, 0.38268339633941650, 0.92387938499450684, 0.0, 0, 0},
191 {-0.14644658565521240, 0.35355338454246521, 0.85355329513549805, 0.35355332493782043, 0, 0},
192 {-0.35355338454246521, 0.14644658565521240, 0.35355332493782043, 0.85355329513549805, 0, 0},
193 {-0.49999991059303284, 0.49999991059303284, 0.49999985098838806, 0.49999985098838806, 0, 0},
194 {-0.27059799432754517, 0.65328145027160645, 0.65328139066696167, 0.27059799432754517, 0, 0},
195 {-0.65328145027160645, 0.27059799432754517, 0.27059799432754517, 0.65328139066696167, 0, 0},
196 {-0.65328133106231689, 0.65328133106231689, 0.27059793472290039, 0.27059793472290039, 0, 0},
197 {-0.92387932538986206, 0.0, 0.0, 0.38268333673477173, 0, 0},
198 {0.0, 0.92387932538986206, 0.38268333673477173, 0.0, 0, 0},
199 {-0.35355329513549805, 0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0, 0},
200 {-0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0.35355329513549805, 0, 0},
201 {-0.38268330693244934, 0.92387938499450684, 0.0, 0.0, 0, 0},
202 {-0.92387938499450684, 0.38268330693244934, 0.0, 0.0, 0, 0},
203 {-COS45, 0.0, 0.0, SIN45, 0, 0},
204 {COS45, 0.0, 0.0, SIN45, 0, 0},
205 {0.0, 0.0, 0.0, 1.0, 0, 0}
206 };
207
208
209 static void viewrotate_apply(ViewOpsData *vod, int x, int y, int ctrl)
210 {
211         View3D *v3d= vod->v3d;
212         int use_sel= 0; /* XXX */
213         
214         v3d->view= 0; /* need to reset everytime because of view snapping */
215         
216         if (U.flag & USER_TRACKBALL) {
217                 float phi, si, q1[4], dvec[3], newvec[3];
218                 
219                 calctrackballvec(&vod->ar->winrct, x, y, newvec);
220         
221                 VecSubf(dvec, newvec, vod->trackvec);
222         
223                 si= sqrt(dvec[0]*dvec[0]+ dvec[1]*dvec[1]+ dvec[2]*dvec[2]);
224                 si/= (2.0*TRACKBALLSIZE);
225         
226                 Crossf(q1+1, vod->trackvec, newvec);
227                 Normalize(q1+1);
228                 
229                 /* Allow for rotation beyond the interval
230                         * [-pi, pi] */
231                 while (si > 1.0)
232                         si -= 2.0;
233                 
234                 /* This relation is used instead of
235                         * phi = asin(si) so that the angle
236                         * of rotation is linearly proportional
237                         * to the distance that the mouse is
238                         * dragged. */
239                 phi = si * M_PI / 2.0;
240                 
241                 si= sin(phi);
242                 q1[0]= cos(phi);
243                 q1[1]*= si;
244                 q1[2]*= si;
245                 q1[3]*= si;     
246                 QuatMul(v3d->viewquat, q1, vod->oldquat);
247                 
248                 if (use_sel) {
249                         /* compute the post multiplication quat, to rotate the offset correctly */
250                         QUATCOPY(q1, vod->oldquat);
251                         QuatConj(q1);
252                         QuatMul(q1, q1, v3d->viewquat);
253                         
254                         QuatConj(q1); /* conj == inv for unit quat */
255                         VECCOPY(v3d->ofs, vod->ofs);
256                         VecSubf(v3d->ofs, v3d->ofs, vod->obofs);
257                         QuatMulVecf(q1, v3d->ofs);
258                         VecAddf(v3d->ofs, v3d->ofs, vod->obofs);
259                 }
260         } 
261         else {
262                 /* New turntable view code by John Aughey */
263                 float si, phi, q1[4];
264                 float m[3][3];
265                 float m_inv[3][3];
266                 float xvec[3] = {1,0,0};
267                 /* Sensitivity will control how fast the viewport rotates.  0.0035 was
268                         obtained experimentally by looking at viewport rotation sensitivities
269                         on other modeling programs. */
270                 /* Perhaps this should be a configurable user parameter. */
271                 const float sensitivity = 0.0035;
272                 
273                 /* Get the 3x3 matrix and its inverse from the quaternion */
274                 QuatToMat3(v3d->viewquat, m);
275                 Mat3Inv(m_inv,m);
276                 
277                 /* Determine the direction of the x vector (for rotating up and down) */
278                 /* This can likely be compuated directly from the quaternion. */
279                 Mat3MulVecfl(m_inv,xvec);
280                 
281                 /* Perform the up/down rotation */
282                 phi = sensitivity * -(y - vod->oldy);
283                 si = sin(phi);
284                 q1[0] = cos(phi);
285                 q1[1] = si * xvec[0];
286                 q1[2] = si * xvec[1];
287                 q1[3] = si * xvec[2];
288                 QuatMul(v3d->viewquat, v3d->viewquat, q1);
289                 
290                 if (use_sel) {
291                         QuatConj(q1); /* conj == inv for unit quat */
292                         VecSubf(v3d->ofs, v3d->ofs, vod->obofs);
293                         QuatMulVecf(q1, v3d->ofs);
294                         VecAddf(v3d->ofs, v3d->ofs, vod->obofs);
295                 }
296                 
297                 /* Perform the orbital rotation */
298                 phi = sensitivity * vod->reverse * (x - vod->oldx);
299                 q1[0] = cos(phi);
300                 q1[1] = q1[2] = 0.0;
301                 q1[3] = sin(phi);
302                 QuatMul(v3d->viewquat, v3d->viewquat, q1);
303                 
304                 if (use_sel) {
305                         QuatConj(q1);
306                         VecSubf(v3d->ofs, v3d->ofs, vod->obofs);
307                         QuatMulVecf(q1, v3d->ofs);
308                         VecAddf(v3d->ofs, v3d->ofs, vod->obofs);
309                 }
310         }
311         
312         /* check for view snap */
313         if (ctrl){
314                 int i;
315                 float viewmat[3][3];
316                 
317                 
318                 QuatToMat3(v3d->viewquat, viewmat);
319                 
320                 for (i = 0 ; i < 39; i++){
321                         float snapmat[3][3];
322                         float view = (int)snapquats[i][4];
323                         float oposite_dir = (int)snapquats[i][5];
324                         
325                         QuatToMat3(snapquats[i], snapmat);
326                         
327                         if ((Inpf(snapmat[0], viewmat[0]) > thres) &&
328                                 (Inpf(snapmat[1], viewmat[1]) > thres) &&
329                                 (Inpf(snapmat[2], viewmat[2]) > thres)){
330                                 
331                                 QUATCOPY(v3d->viewquat, snapquats[i]);
332                                 
333                                 v3d->view = view;
334                                 if (view){
335                                         if (oposite_dir){
336                                                 v3d->flag2 |= V3D_OPP_DIRECTION_NAME;
337                                         }else{
338                                                 v3d->flag2 &= ~V3D_OPP_DIRECTION_NAME;
339                                         }
340                                 }
341                                 
342                                 break;
343                         }
344                 }
345         }
346         vod->oldx= x;
347         vod->oldy= y;
348
349         ED_region_tag_redraw(vod->ar);
350 }
351
352 static int viewrotate_modal(bContext *C, wmOperator *op, wmEvent *event)
353 {
354         ViewOpsData *vod= op->customdata;
355
356         /* execute the events */
357         switch(event->type) {
358                 case MOUSEMOVE:
359                         viewrotate_apply(vod, event->x, event->y, event->ctrl);
360                         break;
361                         
362                 default:
363                         if(event->type==vod->origkey && event->val==0) {
364                                 
365                                 MEM_freeN(vod);
366                                 op->customdata= NULL;
367                                 
368                                 return OPERATOR_FINISHED;
369                         }
370         }
371         
372         return OPERATOR_RUNNING_MODAL;
373 }
374
375 static int viewrotate_invoke(bContext *C, wmOperator *op, wmEvent *event)
376 {
377         ViewOpsData *vod;
378         
379         /* makes op->customdata */
380         viewops_data(C, op, event);
381         vod= op->customdata;
382         
383         /* switch from camera view when: */
384         if(vod->v3d->persp != V3D_PERSP) {
385                 
386                 if (U.uiflag & USER_AUTOPERSP) 
387                         vod->v3d->persp= V3D_PERSP;
388                 else if(vod->v3d->persp==V3D_CAMOB)
389                         vod->v3d->persp= V3D_PERSP;
390                 ED_region_tag_redraw(vod->ar);
391         }
392         
393         /* add temp handler */
394         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
395         
396         return OPERATOR_RUNNING_MODAL;
397 }
398
399
400 void ED_VIEW3D_OT_viewrotate(wmOperatorType *ot)
401 {
402         
403         /* identifiers */
404         ot->name= "Rotate view";
405         ot->idname= "ED_VIEW3D_OT_viewrotate";
406         
407         /* api callbacks */
408         ot->invoke= viewrotate_invoke;
409         ot->modal= viewrotate_modal;
410         ot->poll= ED_operator_areaactive;
411 }
412
413 /* ************************ viewmove ******************************** */
414
415 static void viewmove_apply(ViewOpsData *vod, int x, int y)
416 {
417         if(vod->v3d->persp==V3D_CAMOB) {
418                 float max= (float)MAX2(vod->ar->winx, vod->ar->winy);
419                 
420                 vod->v3d->camdx += (vod->oldx - x)/(max);
421                 vod->v3d->camdy += (vod->oldy - y)/(max);
422                 CLAMP(vod->v3d->camdx, -1.0f, 1.0f);
423                 CLAMP(vod->v3d->camdy, -1.0f, 1.0f);
424 // XXX          preview3d_event= 0;
425         }
426         else {
427                 float dvec[3];
428                 
429                 window_to_3d(vod->ar, vod->v3d, dvec, x-vod->oldx, y-vod->oldy);
430                 VecAddf(vod->v3d->ofs, vod->v3d->ofs, dvec);
431         }
432         
433         vod->oldx= x;
434         vod->oldy= y;
435         
436         ED_region_tag_redraw(vod->ar);
437 }
438
439
440 static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
441 {       
442         ViewOpsData *vod= op->customdata;
443         
444         /* execute the events */
445         switch(event->type) {
446                 case MOUSEMOVE:
447                         viewmove_apply(vod, event->x, event->y);
448                         break;
449                         
450                 default:
451                         if(event->type==vod->origkey && event->val==0) {
452                                 
453                                 MEM_freeN(vod);
454                                 op->customdata= NULL;
455                                 
456                                 return OPERATOR_FINISHED;
457                         }
458         }
459         
460         return OPERATOR_RUNNING_MODAL;
461 }
462
463 static int viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
464 {
465         /* makes op->customdata */
466         viewops_data(C, op, event);
467         
468         /* add temp handler */
469         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
470         
471         return OPERATOR_RUNNING_MODAL;
472 }
473
474
475 void ED_VIEW3D_OT_viewmove(wmOperatorType *ot)
476 {
477         
478         /* identifiers */
479         ot->name= "Rotate view";
480         ot->idname= "ED_VIEW3D_OT_viewmove";
481         
482         /* api callbacks */
483         ot->invoke= viewmove_invoke;
484         ot->modal= viewmove_modal;
485         ot->poll= ED_operator_areaactive;
486 }
487
488 /* ************************ viewzoom ******************************** */
489
490 static void view_zoom_mouseloc(ARegion *ar, View3D *v3d, float dfac, int mx, int my)
491 {
492         if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
493                 float dvec[3];
494                 float tvec[3];
495                 float tpos[3];
496                 float new_dist;
497                 short vb[2], mouseloc[2];
498                 
499                 mouseloc[0]= mx - ar->winrct.xmin;
500                 mouseloc[1]= my - ar->winrct.ymin;
501                 
502                 /* find the current window width and height */
503                 vb[0] = ar->winx;
504                 vb[1] = ar->winy;
505                 
506                 tpos[0] = -v3d->ofs[0];
507                 tpos[1] = -v3d->ofs[1];
508                 tpos[2] = -v3d->ofs[2];
509                 
510                 /* Project cursor position into 3D space */
511                 initgrabz(v3d, tpos[0], tpos[1], tpos[2]);
512                 window_to_3d(ar, v3d, dvec, mouseloc[0]-vb[0]/2, mouseloc[1]-vb[1]/2);
513                 
514                 /* Calculate view target position for dolly */
515                 tvec[0] = -(tpos[0] + dvec[0]);
516                 tvec[1] = -(tpos[1] + dvec[1]);
517                 tvec[2] = -(tpos[2] + dvec[2]);
518                 
519                 /* Offset to target position and dolly */
520                 new_dist = v3d->dist * dfac;
521                 
522                 VECCOPY(v3d->ofs, tvec);
523                 v3d->dist = new_dist;
524                 
525                 /* Calculate final offset */
526                 dvec[0] = tvec[0] + dvec[0] * dfac;
527                 dvec[1] = tvec[1] + dvec[1] * dfac;
528                 dvec[2] = tvec[2] + dvec[2] * dfac;
529                 
530                 VECCOPY(v3d->ofs, dvec);
531         } else {
532                 v3d->dist *= dfac;
533         }
534 }
535
536
537 static void viewzoom_apply(ViewOpsData *vod, int x, int y)
538 {
539         float zfac=1.0;
540
541         if(U.viewzoom==USER_ZOOM_CONT) {
542                 // oldstyle zoom
543                 zfac = 1.0+(float)(vod->origx - x + vod->origy - y)/1000.0;
544         }
545         else if(U.viewzoom==USER_ZOOM_SCALE) {
546                 int ctr[2], len1, len2;
547                 // method which zooms based on how far you move the mouse
548                 
549                 ctr[0] = (vod->ar->winrct.xmax + vod->ar->winrct.xmin)/2;
550                 ctr[1] = (vod->ar->winrct.ymax + vod->ar->winrct.ymin)/2;
551                 
552                 len1 = (int)sqrt((ctr[0] - x)*(ctr[0] - x) + (ctr[1] - y)*(ctr[1] - y)) + 5;
553                 len2 = (int)sqrt((ctr[0] - vod->origx)*(ctr[0] - vod->origx) + (ctr[1] - vod->origy)*(ctr[1] - vod->origy)) + 5;
554                 
555                 zfac = vod->dist0 * ((float)len2/len1) / vod->v3d->dist;
556         }
557         else {  /* USER_ZOOM_DOLLY */
558                 float len1 = (vod->ar->winrct.ymax - y) + 5;
559                 float len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
560                 zfac = vod->dist0 * (2.0*((len2/len1)-1.0) + 1.0) / vod->v3d->dist;
561         }
562
563         if(zfac != 1.0 && zfac*vod->v3d->dist > 0.001*vod->v3d->grid && 
564                                 zfac*vod->v3d->dist < 10.0*vod->v3d->far)
565                 view_zoom_mouseloc(vod->ar, vod->v3d, zfac, vod->oldx, vod->oldy);
566
567
568         if ((U.uiflag & USER_ORBIT_ZBUF) && (U.viewzoom==USER_ZOOM_CONT) && (vod->v3d->persp==V3D_PERSP)) {
569                 float upvec[3], mat[3][3];
570                 
571                 /* Secret apricot feature, translate the view when in continues mode */
572                 upvec[0] = upvec[1] = 0.0f;
573                 upvec[2] = (vod->dist0 - vod->v3d->dist) * vod->v3d->grid;
574                 vod->v3d->dist = vod->dist0;
575                 Mat3CpyMat4(mat, vod->v3d->viewinv);
576                 Mat3MulVecfl(mat, upvec);
577                 VecAddf(vod->v3d->ofs, vod->v3d->ofs, upvec);
578         } else {
579                 /* these limits are in toets.c too */
580                 if(vod->v3d->dist<0.001*vod->v3d->grid) vod->v3d->dist= 0.001*vod->v3d->grid;
581                 if(vod->v3d->dist>10.0*vod->v3d->far) vod->v3d->dist=10.0*vod->v3d->far;
582         }
583
584 // XXX  if(vod->v3d->persp==V3D_ORTHO || vod->v3d->persp==V3D_CAMOB) preview3d_event= 0;
585         
586         ED_region_tag_redraw(vod->ar);
587 }
588
589
590 static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event)
591 {       
592         ViewOpsData *vod= op->customdata;
593         
594         /* execute the events */
595         switch(event->type) {
596                 case MOUSEMOVE:
597                         viewzoom_apply(vod, event->x, event->y);
598                         break;
599                         
600                 default:
601                         if(event->type==vod->origkey && event->val==0) {
602                                 
603                                 MEM_freeN(vod);
604                                 op->customdata= NULL;
605                                 
606                                 return OPERATOR_FINISHED;
607                         }
608         }
609         
610         return OPERATOR_RUNNING_MODAL;
611 }
612
613 static int viewzoom_exec(bContext *C, wmOperator *op)
614 {
615         ScrArea *sa= CTX_wm_area(C);
616         View3D *v3d= sa->spacedata.first;
617         int delta= RNA_int_get(op->ptr, "delta");
618
619         if(delta < 0) {
620                 /* this min and max is also in viewmove() */
621                 if(v3d->persp==V3D_CAMOB) {
622                         v3d->camzoom-= 10;
623                         if(v3d->camzoom<-30) v3d->camzoom= -30;
624                 }
625                 else if(v3d->dist<10.0*v3d->far) v3d->dist*=1.2f;
626         }
627         else {
628                 if(v3d->persp==V3D_CAMOB) {
629                         v3d->camzoom+= 10;
630                         if(v3d->camzoom>300) v3d->camzoom= 300;
631                 }
632                 else if(v3d->dist> 0.001*v3d->grid) v3d->dist*=.83333f;
633         }
634         
635         ED_region_tag_redraw(CTX_wm_region(C));
636         
637         return OPERATOR_FINISHED;
638 }
639
640 static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
641 {
642         int delta= RNA_int_get(op->ptr, "delta");
643
644         if(delta) {
645                 viewzoom_exec(C, op);
646         }
647         else {
648                 /* makes op->customdata */
649                 viewops_data(C, op, event);
650                 
651                 /* add temp handler */
652                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
653                 
654                 return OPERATOR_RUNNING_MODAL;
655         }
656         return OPERATOR_FINISHED;
657 }
658
659
660 void ED_VIEW3D_OT_viewzoom(wmOperatorType *ot)
661 {
662         
663         /* identifiers */
664         ot->name= "Rotate view";
665         ot->idname= "ED_VIEW3D_OT_viewzoom";
666         
667         /* api callbacks */
668         ot->invoke= viewzoom_invoke;
669         ot->exec= viewzoom_exec;
670         ot->modal= viewzoom_modal;
671         ot->poll= ED_operator_areaactive;
672         
673         RNA_def_property(ot->srna, "delta", PROP_INT, PROP_NONE);
674 }
675
676 static int viewhome_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
677 {
678         ScrArea *sa= CTX_wm_area(C);
679         ARegion *ar= CTX_wm_region(C);
680         View3D *v3d= sa->spacedata.first;
681         Scene *scene= CTX_data_scene(C);
682         Base *base;
683
684         int center= RNA_boolean_get(op->ptr, "center");
685         
686         float size, min[3], max[3], afm[3];
687         int ok= 1, onedone=0;
688
689         if(center) {
690                 min[0]= min[1]= min[2]= 0.0f;
691                 max[0]= max[1]= max[2]= 0.0f;
692         }
693         else {
694                 INIT_MINMAX(min, max);
695         }
696         
697         for(base= scene->base.first; base; base= base->next) {
698                 if(base->lay & v3d->lay) {
699                         onedone= 1;
700                         minmax_object(base->object, min, max);
701                 }
702         }
703         if(!onedone) return OPERATOR_FINISHED; /* TODO - should this be cancel? */
704         
705         afm[0]= (max[0]-min[0]);
706         afm[1]= (max[1]-min[1]);
707         afm[2]= (max[2]-min[2]);
708         size= 0.7f*MAX3(afm[0], afm[1], afm[2]);
709         if(size==0.0) ok= 0;
710                 
711         if(ok) {
712                 float new_dist;
713                 float new_ofs[3];
714                 
715                 new_dist = size;
716                 new_ofs[0]= -(min[0]+max[0])/2.0f;
717                 new_ofs[1]= -(min[1]+max[1])/2.0f;
718                 new_ofs[2]= -(min[2]+max[2])/2.0f;
719                 
720                 // correction for window aspect ratio
721                 if(ar->winy>2 && ar->winx>2) {
722                         size= (float)ar->winx/(float)ar->winy;
723                         if(size<1.0) size= 1.0f/size;
724                         new_dist*= size;
725                 }
726                 
727                 if (v3d->persp==V3D_CAMOB && v3d->camera) {
728                         /* switch out of camera view */
729                         float orig_lens= v3d->lens;
730                         
731                         v3d->persp= V3D_PERSP;
732                         v3d->dist= 0.0;
733                         view_settings_from_ob(v3d->camera, v3d->ofs, NULL, NULL, &v3d->lens);
734                         smooth_view(v3d, new_ofs, NULL, &new_dist, &orig_lens); /* TODO - this dosnt work yet */
735                         
736                 } else {
737                         if(v3d->persp==V3D_CAMOB) v3d->persp= V3D_PERSP;
738                         smooth_view(v3d, new_ofs, NULL, &new_dist, NULL); /* TODO - this dosnt work yet */
739                 }
740         }
741 // XXX  BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
742
743         ED_region_tag_redraw(ar);
744
745         return OPERATOR_FINISHED;
746 }
747
748 void ED_VIEW3D_OT_viewhome(wmOperatorType *ot)
749 {
750
751         /* identifiers */
752         ot->name= "View home";
753         ot->idname= "ED_VIEW3D_OT_viewhome";
754
755         /* api callbacks */
756         ot->exec= viewhome_exec;
757         ot->poll= ED_operator_areaactive;
758
759         RNA_def_property(ot->srna, "center", PROP_BOOLEAN, PROP_NONE);
760 }
761
762 static int viewcenter_exec(bContext *C, wmOperator *op) /* like a localview without local!, was centerview() in 2.4x */
763 {       
764         ScrArea *sa= CTX_wm_area(C);
765         ARegion *ar= CTX_wm_region(C);
766         View3D *v3d= sa->spacedata.first;
767         Scene *scene= CTX_data_scene(C);
768         Object *ob= OBACT;
769         float size, min[3], max[3], afm[3];
770         int ok=0;
771
772         /* SMOOTHVIEW */
773         float new_ofs[3];
774         float new_dist;
775
776         INIT_MINMAX(min, max);
777
778         if (G.f & G_WEIGHTPAINT) {
779                 /* hardcoded exception, we look for the one selected armature */
780                 /* this is weak code this way, we should make a generic active/selection callback interface once... */
781                 Base *base;
782                 for(base=scene->base.first; base; base= base->next) {
783                         if(TESTBASELIB(v3d, base)) {
784                                 if(base->object->type==OB_ARMATURE)
785                                         if(base->object->flag & OB_POSEMODE)
786                                                 break;
787                         }
788                 }
789                 if(base)
790                         ob= base->object;
791         }
792
793
794         if(G.obedit) {
795 // XXX          ok = minmax_verts(min, max);    /* only selected */
796         }
797         else if(ob && (ob->flag & OB_POSEMODE)) {
798                 if(ob->pose) {
799                         bArmature *arm= ob->data;
800                         bPoseChannel *pchan;
801                         float vec[3];
802
803                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
804                                 if(pchan->bone->flag & BONE_SELECTED) {
805                                         if(pchan->bone->layer & arm->layer) {
806                                                 ok= 1;
807                                                 VECCOPY(vec, pchan->pose_head);
808                                                 Mat4MulVecfl(ob->obmat, vec);
809                                                 DO_MINMAX(vec, min, max);
810                                                 VECCOPY(vec, pchan->pose_tail);
811                                                 Mat4MulVecfl(ob->obmat, vec);
812                                                 DO_MINMAX(vec, min, max);
813                                         }
814                                 }
815                         }
816                 }
817         }
818         else if (FACESEL_PAINT_TEST) {
819 // XXX          ok= minmax_tface(min, max);
820         }
821         else if (G.f & G_PARTICLEEDIT) {
822 // XXX          ok= PE_minmax(min, max);
823         }
824         else {
825                 Base *base= FIRSTBASE;
826                 while(base) {
827                         if(TESTBASE(v3d, base))  {
828                                 minmax_object(base->object, min, max);
829                                 /* account for duplis */
830                                 minmax_object_duplis(base->object, min, max);
831
832                                 ok= 1;
833                         }
834                         base= base->next;
835                 }
836         }
837
838         if(ok==0) return OPERATOR_FINISHED;
839
840         afm[0]= (max[0]-min[0]);
841         afm[1]= (max[1]-min[1]);
842         afm[2]= (max[2]-min[2]);
843         size= 0.7f*MAX3(afm[0], afm[1], afm[2]);
844
845         if(size <= v3d->near*1.5f) size= v3d->near*1.5f;
846
847         new_ofs[0]= -(min[0]+max[0])/2.0f;
848         new_ofs[1]= -(min[1]+max[1])/2.0f;
849         new_ofs[2]= -(min[2]+max[2])/2.0f;
850
851         new_dist = size;
852
853         /* correction for window aspect ratio */
854         if(ar->winy>2 && ar->winx>2) {
855                 size= (float)ar->winx/(float)ar->winy;
856                 if(size<1.0f) size= 1.0f/size;
857                 new_dist*= size;
858         }
859
860         v3d->cursor[0]= -new_ofs[0];
861         v3d->cursor[1]= -new_ofs[1];
862         v3d->cursor[2]= -new_ofs[2];
863
864         if (v3d->persp==V3D_CAMOB && v3d->camera) {
865                 float orig_lens= v3d->lens;
866
867                 v3d->persp=V3D_PERSP;
868                 v3d->dist= 0.0f;
869                 view_settings_from_ob(v3d->camera, v3d->ofs, NULL, NULL, &v3d->lens);
870                 smooth_view(v3d, new_ofs, NULL, &new_dist, &orig_lens);
871         } else {
872                 if(v3d->persp==V3D_CAMOB)
873                         v3d->persp= V3D_PERSP;
874
875                 smooth_view(v3d, new_ofs, NULL, &new_dist, NULL);
876         }
877
878 // XXX  BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
879
880         ED_region_tag_redraw(ar);
881
882         return OPERATOR_FINISHED;
883 }
884
885 void ED_VIEW3D_OT_viewcenter(wmOperatorType *ot)
886 {
887
888         /* identifiers */
889         ot->name= "View center";
890         ot->idname= "ED_VIEW3D_OT_viewcenter";
891
892         /* api callbacks */
893         ot->exec= viewcenter_exec;
894         ot->poll= ED_operator_areaactive;
895 }
896
897
898 /* ************************* below the line! *********************** */
899
900
901 /* XXX todo Zooms in on a border drawn by the user */
902 int view_autodist(Scene *scene, ARegion *ar, View3D *v3d, short *mval, float mouse_worldloc[3] ) //, float *autodist )
903 {
904         rcti rect;
905         /* ZBuffer depth vars */
906         bglMats mats;
907         float depth, depth_close= MAXFLOAT;
908         int had_depth = 0;
909         double cent[2],  p[3];
910         int xs, ys;
911         
912         // XXX          getmouseco_areawin(mval);
913         
914         // XXX  persp(PERSP_VIEW);
915         
916         rect.xmax = mval[0] + 4;
917         rect.ymax = mval[1] + 4;
918         
919         rect.xmin = mval[0] - 4;
920         rect.ymin = mval[1] - 4;
921         
922         /* Get Z Depths, needed for perspective, nice for ortho */
923         bgl_get_mats(&mats);
924         draw_depth(scene, ar, v3d, NULL);
925         
926         /* force updating */
927         if (v3d->depths) {
928                 had_depth = 1;
929                 v3d->depths->damaged = 1;
930         }
931         
932         view3d_update_depths(ar, v3d);
933         
934         /* Constrain rect to depth bounds */
935         if (rect.xmin < 0) rect.xmin = 0;
936         if (rect.ymin < 0) rect.ymin = 0;
937         if (rect.xmax >= v3d->depths->w) rect.xmax = v3d->depths->w-1;
938         if (rect.ymax >= v3d->depths->h) rect.ymax = v3d->depths->h-1;          
939         
940         /* Find the closest Z pixel */
941         for (xs=rect.xmin; xs < rect.xmax; xs++) {
942                 for (ys=rect.ymin; ys < rect.ymax; ys++) {
943                         depth= v3d->depths->depths[ys*v3d->depths->w+xs];
944                         if(depth < v3d->depths->depth_range[1] && depth > v3d->depths->depth_range[0]) {
945                                 if (depth_close > depth) {
946                                         depth_close = depth;
947                                 }
948                         }
949                 }
950         }
951         
952         if (depth_close==MAXFLOAT)
953                 return 0;
954         
955         if (had_depth==0) {
956                 MEM_freeN(v3d->depths->depths);
957                 v3d->depths->depths = NULL;
958         }
959         v3d->depths->damaged = 1;
960         
961         cent[0] = (double)mval[0];
962         cent[1] = (double)mval[1];
963         
964         if (!gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
965                 return 0;
966         
967         mouse_worldloc[0] = (float)p[0];
968         mouse_worldloc[1] = (float)p[1];
969         mouse_worldloc[2] = (float)p[2];
970         return 1;
971 }
972
973
974
975 /* ********************* NDOF ************************ */
976 /* note: this code is confusing and unclear... (ton) */
977 /* **************************************************** */
978
979 // ndof scaling will be moved to user setting.
980 // In the mean time this is just a place holder.
981
982 // Note: scaling in the plugin and ghostwinlay.c
983 // should be removed. With driver default setting,
984 // each axis returns approx. +-200 max deflection.
985
986 // The values I selected are based on the older
987 // polling i/f. With event i/f, the sensistivity
988 // can be increased for improved response from
989 // small deflections of the device input.
990
991
992 // lukep notes : i disagree on the range.
993 // the normal 3Dconnection driver give +/-400
994 // on defaut range in other applications
995 // and up to +/- 1000 if set to maximum
996 // because i remove the scaling by delta,
997 // which was a bad idea as it depend of the system
998 // speed and os, i changed the scaling values, but 
999 // those are still not ok
1000
1001
1002 float ndof_axis_scale[6] = {
1003         +0.01,  // Tx
1004         +0.01,  // Tz
1005         +0.01,  // Ty
1006         +0.0015,        // Rx
1007         +0.0015,        // Rz
1008         +0.0015 // Ry
1009 };
1010
1011 void filterNDOFvalues(float *sbval)
1012 {
1013         int i=0;
1014         float max  = 0.0;
1015         
1016         for (i =0; i<6;i++)
1017                 if (fabs(sbval[i]) > max)
1018                         max = fabs(sbval[i]);
1019         for (i =0; i<6;i++)
1020                 if (fabs(sbval[i]) != max )
1021                         sbval[i]=0.0;
1022 }
1023
1024 // statics for controlling v3d->dist corrections.
1025 // viewmoveNDOF zeros and adjusts v3d->ofs.
1026 // viewmove restores based on dz_flag state.
1027
1028 int dz_flag = 0;
1029 float m_dist;
1030
1031 void viewmoveNDOFfly(ARegion *ar, View3D *v3d, int mode)
1032 {
1033     int i;
1034     float phi;
1035     float dval[7];
1036         // static fval[6] for low pass filter; device input vector is dval[6]
1037         static float fval[6];
1038     float tvec[3],rvec[3];
1039     float q1[4];
1040         float mat[3][3];
1041         float upvec[3];
1042
1043
1044     /*----------------------------------------------------
1045          * sometimes this routine is called from headerbuttons
1046      * viewmove needs to refresh the screen
1047      */
1048 // XXX  areawinset(ar->win);
1049
1050
1051         // fetch the current state of the ndof device
1052 // XXX  getndof(dval);
1053
1054         if (v3d->ndoffilter)
1055                 filterNDOFvalues(fval);
1056
1057         // Scale input values
1058
1059 //      if(dval[6] == 0) return; // guard against divide by zero
1060
1061         for(i=0;i<6;i++) {
1062
1063                 // user scaling
1064                 dval[i] = dval[i] * ndof_axis_scale[i];
1065         }
1066
1067
1068         // low pass filter with zero crossing reset
1069
1070         for(i=0;i<6;i++) {
1071                 if((dval[i] * fval[i]) >= 0)
1072                         dval[i] = (fval[i] * 15 + dval[i]) / 16;
1073                 else
1074                         fval[i] = 0;
1075         }
1076
1077
1078         // force perspective mode. This is a hack and is
1079         // incomplete. It doesn't actually effect the view
1080         // until the first draw and doesn't update the menu
1081         // to reflect persp mode.
1082
1083         v3d->persp = V3D_PERSP;
1084
1085
1086         // Correct the distance jump if v3d->dist != 0
1087
1088         // This is due to a side effect of the original
1089         // mouse view rotation code. The rotation point is
1090         // set a distance in front of the viewport to
1091         // make rotating with the mouse look better.
1092         // The distance effect is written at a low level
1093         // in the view management instead of the mouse
1094         // view function. This means that all other view
1095         // movement devices must subtract this from their
1096         // view transformations.
1097
1098         if(v3d->dist != 0.0) {
1099                 dz_flag = 1;
1100                 m_dist = v3d->dist;
1101                 upvec[0] = upvec[1] = 0;
1102                 upvec[2] = v3d->dist;
1103                 Mat3CpyMat4(mat, v3d->viewinv);
1104                 Mat3MulVecfl(mat, upvec);
1105                 VecSubf(v3d->ofs, v3d->ofs, upvec);
1106                 v3d->dist = 0.0;
1107         }
1108
1109
1110         // Apply rotation
1111         // Rotations feel relatively faster than translations only in fly mode, so
1112         // we have no choice but to fix that here (not in the plugins)
1113         rvec[0] = -0.5 * dval[3];
1114         rvec[1] = -0.5 * dval[4];
1115         rvec[2] = -0.5 * dval[5];
1116
1117         // rotate device x and y by view z
1118
1119         Mat3CpyMat4(mat, v3d->viewinv);
1120         mat[2][2] = 0.0f;
1121         Mat3MulVecfl(mat, rvec);
1122
1123         // rotate the view
1124
1125         phi = Normalize(rvec);
1126         if(phi != 0) {
1127                 VecRotToQuat(rvec,phi,q1);
1128                 QuatMul(v3d->viewquat, v3d->viewquat, q1);
1129         }
1130
1131
1132         // Apply translation
1133
1134         tvec[0] = dval[0];
1135         tvec[1] = dval[1];
1136         tvec[2] = -dval[2];
1137
1138         // the next three lines rotate the x and y translation coordinates
1139         // by the current z axis angle
1140
1141         Mat3CpyMat4(mat, v3d->viewinv);
1142         mat[2][2] = 0.0f;
1143         Mat3MulVecfl(mat, tvec);
1144
1145         // translate the view
1146
1147         VecSubf(v3d->ofs, v3d->ofs, tvec);
1148
1149
1150         /*----------------------------------------------------
1151      * refresh the screen XXX
1152       */
1153
1154         // update render preview window
1155
1156 // XXX  BIF_view3d_previewrender_signal(ar, PR_DBASE|PR_DISPRECT);
1157 }
1158
1159 void viewmoveNDOF(Scene *scene, View3D *v3d, int mode)
1160 {
1161     float fval[7];
1162     float dvec[3];
1163     float sbadjust = 1.0f;
1164     float len;
1165         short use_sel = 0;
1166         Object *ob = OBACT;
1167     float m[3][3];
1168     float m_inv[3][3];
1169     float xvec[3] = {1,0,0};
1170     float yvec[3] = {0,-1,0};
1171     float zvec[3] = {0,0,1};
1172         float phi, si;
1173     float q1[4];
1174     float obofs[3];
1175     float reverse;
1176     //float diff[4];
1177     float d, curareaX, curareaY;
1178     float mat[3][3];
1179     float upvec[3];
1180
1181     /* Sensitivity will control how fast the view rotates.  The value was
1182      * obtained experimentally by tweaking until the author didn't get dizzy watching.
1183      * Perhaps this should be a configurable user parameter. 
1184      */
1185     float psens = 0.005f * (float) U.ndof_pan;   /* pan sensitivity */
1186     float rsens = 0.005f * (float) U.ndof_rotate;  /* rotate sensitivity */
1187     float zsens = 0.3f;   /* zoom sensitivity */
1188
1189     const float minZoom = -30.0f;
1190     const float maxZoom = 300.0f;
1191
1192         //reset view type
1193         v3d->view = 0;
1194 //printf("passing here \n");
1195 //
1196         if (G.obedit==NULL && ob && !(ob->flag & OB_POSEMODE)) {
1197                 use_sel = 1;
1198         }
1199
1200     if((dz_flag)||v3d->dist==0) {
1201                 dz_flag = 0;
1202                 v3d->dist = m_dist;
1203                 upvec[0] = upvec[1] = 0;
1204                 upvec[2] = v3d->dist;
1205                 Mat3CpyMat4(mat, v3d->viewinv);
1206                 Mat3MulVecfl(mat, upvec);
1207                 VecAddf(v3d->ofs, v3d->ofs, upvec);
1208         }
1209
1210     /*----------------------------------------------------
1211          * sometimes this routine is called from headerbuttons
1212      * viewmove needs to refresh the screen
1213      */
1214 // XXX  areawinset(curarea->win);
1215
1216     /*----------------------------------------------------
1217      * record how much time has passed. clamp at 10 Hz
1218      * pretend the previous frame occured at the clamped time 
1219      */
1220 //    now = PIL_check_seconds_timer();
1221  //   frametime = (now - prevTime);
1222  //   if (frametime > 0.1f){        /* if more than 1/10s */
1223  //       frametime = 1.0f/60.0;      /* clamp at 1/60s so no jumps when starting to move */
1224 //    }
1225 //    prevTime = now;
1226  //   sbadjust *= 60 * frametime;             /* normalize ndof device adjustments to 100Hz for framerate independence */
1227
1228     /* fetch the current state of the ndof device & enforce dominant mode if selected */
1229 // XXX    getndof(fval);
1230         if (v3d->ndoffilter)
1231                 filterNDOFvalues(fval);
1232         
1233         
1234     // put scaling back here, was previously in ghostwinlay
1235     fval[0] = fval[0] * (1.0f/600.0f);
1236     fval[1] = fval[1] * (1.0f/600.0f);
1237     fval[2] = fval[2] * (1.0f/1100.0f);
1238     fval[3] = fval[3] * 0.00005f;
1239     fval[4] =-fval[4] * 0.00005f;
1240     fval[5] = fval[5] * 0.00005f;
1241     fval[6] = fval[6] / 1000000.0f;
1242                         
1243     // scale more if not in perspective mode
1244     if (v3d->persp == V3D_ORTHO) {
1245         fval[0] = fval[0] * 0.05f;
1246         fval[1] = fval[1] * 0.05f;
1247         fval[2] = fval[2] * 0.05f;
1248         fval[3] = fval[3] * 0.9f;
1249         fval[4] = fval[4] * 0.9f;
1250         fval[5] = fval[5] * 0.9f;
1251         zsens *= 8;
1252     }
1253                         
1254         
1255     /* set object offset */
1256         if (ob) {
1257                 obofs[0] = -ob->obmat[3][0];
1258                 obofs[1] = -ob->obmat[3][1];
1259                 obofs[2] = -ob->obmat[3][2];
1260         }
1261         else {
1262                 VECCOPY(obofs, v3d->ofs);
1263         }
1264
1265     /* calc an adjustment based on distance from camera
1266        disabled per patch 14402 */
1267      d = 1.0f;
1268
1269 /*    if (ob) {
1270         VecSubf(diff, obofs, v3d->ofs);
1271         d = VecLength(diff);
1272     }
1273 */
1274
1275     reverse = (v3d->persmat[2][1] < 0.0f) ? -1.0f : 1.0f;
1276
1277     /*----------------------------------------------------
1278      * ndof device pan 
1279      */
1280     psens *= 1.0f + d;
1281     curareaX = sbadjust * psens * fval[0];
1282     curareaY = sbadjust * psens * fval[1];
1283     dvec[0] = curareaX * v3d->persinv[0][0] + curareaY * v3d->persinv[1][0];
1284     dvec[1] = curareaX * v3d->persinv[0][1] + curareaY * v3d->persinv[1][1];
1285     dvec[2] = curareaX * v3d->persinv[0][2] + curareaY * v3d->persinv[1][2];
1286     VecAddf(v3d->ofs, v3d->ofs, dvec);
1287
1288     /*----------------------------------------------------
1289      * ndof device dolly 
1290      */
1291     len = zsens * sbadjust * fval[2];
1292
1293     if (v3d->persp==V3D_CAMOB) {
1294         if(v3d->persp==V3D_CAMOB) { /* This is stupid, please fix - TODO */
1295             v3d->camzoom+= 10.0f * -len;
1296         }
1297         if (v3d->camzoom < minZoom) v3d->camzoom = minZoom;
1298         else if (v3d->camzoom > maxZoom) v3d->camzoom = maxZoom;
1299     }
1300     else if ((v3d->dist> 0.001*v3d->grid) && (v3d->dist<10.0*v3d->far)) {
1301         v3d->dist*=(1.0 + len);
1302     }
1303
1304
1305     /*----------------------------------------------------
1306      * ndof device turntable
1307      * derived from the turntable code in viewmove
1308      */
1309
1310     /* Get the 3x3 matrix and its inverse from the quaternion */
1311     QuatToMat3(v3d->viewquat, m);
1312     Mat3Inv(m_inv,m);
1313
1314     /* Determine the direction of the x vector (for rotating up and down) */
1315     /* This can likely be compuated directly from the quaternion. */
1316     Mat3MulVecfl(m_inv,xvec);
1317     Mat3MulVecfl(m_inv,yvec);
1318     Mat3MulVecfl(m_inv,zvec);
1319
1320     /* Perform the up/down rotation */
1321     phi = sbadjust * rsens * /*0.5f * */ fval[3]; /* spin vertically half as fast as horizontally */
1322     si = sin(phi);
1323     q1[0] = cos(phi);
1324     q1[1] = si * xvec[0];
1325     q1[2] = si * xvec[1];
1326     q1[3] = si * xvec[2];
1327     QuatMul(v3d->viewquat, v3d->viewquat, q1);
1328
1329     if (use_sel) {
1330         QuatConj(q1); /* conj == inv for unit quat */
1331         VecSubf(v3d->ofs, v3d->ofs, obofs);
1332         QuatMulVecf(q1, v3d->ofs);
1333         VecAddf(v3d->ofs, v3d->ofs, obofs);
1334     }
1335
1336     /* Perform the orbital rotation */
1337     /* Perform the orbital rotation 
1338        If the seen Up axis is parallel to the zoom axis, rotation should be
1339        achieved with a pure Roll motion (no Spin) on the device. When you start 
1340        to tilt, moving from Top to Side view, Spinning will increasingly become 
1341        more relevant while the Roll component will decrease. When a full 
1342        Side view is reached, rotations around the world's Up axis are achieved
1343        with a pure Spin-only motion.  In other words the control of the spinning
1344        around the world's Up axis should move from the device's Spin axis to the
1345        device's Roll axis depending on the orientation of the world's Up axis 
1346        relative to the screen. */
1347     //phi = sbadjust * rsens * reverse * fval[4];  /* spin the knob, y axis */
1348     phi = sbadjust * rsens * (yvec[2] * fval[4] + zvec[2] * fval[5]);
1349     q1[0] = cos(phi);
1350     q1[1] = q1[2] = 0.0;
1351     q1[3] = sin(phi);
1352     QuatMul(v3d->viewquat, v3d->viewquat, q1);
1353
1354     if (use_sel) {
1355         QuatConj(q1);
1356         VecSubf(v3d->ofs, v3d->ofs, obofs);
1357         QuatMulVecf(q1, v3d->ofs);
1358         VecAddf(v3d->ofs, v3d->ofs, obofs);
1359     }
1360
1361     /*----------------------------------------------------
1362      * refresh the screen
1363      */
1364 // XXX    scrarea_do_windraw(curarea);
1365 }
1366
1367
1368
1369