86957ecb86c4487d5ff5b7be05d36ccef2346475
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_armature_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_camera_types.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_rand.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_context.h"
47 #include "BKE_image.h"
48 #include "BKE_library.h"
49 #include "BKE_object.h"
50 #include "BKE_paint.h"
51 #include "BKE_report.h"
52 #include "BKE_scene.h"
53 #include "BKE_tessmesh.h"
54
55
56 #include "BIF_gl.h"
57 #include "BIF_glutil.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "RNA_access.h"
63 #include "RNA_define.h"
64
65 #include "ED_particle.h"
66 #include "ED_screen.h"
67 #include "ED_transform.h"
68 #include "ED_mesh.h"
69 #include "ED_view3d.h"
70
71
72 #include "PIL_time.h" /* smoothview */
73
74 #include "view3d_intern.h"      // own include
75
76 /* ********************** view3d_edit: view manipulations ********************* */
77
78 /* ********************* box view support ***************** */
79
80 static void view3d_boxview_clip(ScrArea *sa)
81 {
82         ARegion *ar;
83         BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
84         float clip[6][4];
85         float x1= 0.0f, y1= 0.0f, z1= 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
86         int val;
87
88         /* create bounding box */
89         for(ar= sa->regionbase.first; ar; ar= ar->next) {
90                 if(ar->regiontype==RGN_TYPE_WINDOW) {
91                         RegionView3D *rv3d= ar->regiondata;
92
93                         if(rv3d->viewlock & RV3D_BOXCLIP) {
94                                 if(ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
95                                         if(ar->winx>ar->winy) x1= rv3d->dist;
96                                         else x1= ar->winx*rv3d->dist/ar->winy;
97
98                                         if(ar->winx>ar->winy) y1= ar->winy*rv3d->dist/ar->winx;
99                                         else y1= rv3d->dist;
100                                         copy_v2_v2(ofs, rv3d->ofs);
101                                 }
102                                 else if(ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
103                                         ofs[2]= rv3d->ofs[2];
104
105                                         if(ar->winx>ar->winy) z1= ar->winy*rv3d->dist/ar->winx;
106                                         else z1= rv3d->dist;
107                                 }
108                         }
109                 }
110         }
111
112         for(val=0; val<8; val++) {
113                 if(ELEM4(val, 0, 3, 4, 7))
114                         bb->vec[val][0]= -x1 - ofs[0];
115                 else
116                         bb->vec[val][0]=  x1 - ofs[0];
117
118                 if(ELEM4(val, 0, 1, 4, 5))
119                         bb->vec[val][1]= -y1 - ofs[1];
120                 else
121                         bb->vec[val][1]=  y1 - ofs[1];
122
123                 if(val > 3)
124                         bb->vec[val][2]= -z1 - ofs[2];
125                 else
126                         bb->vec[val][2]=  z1 - ofs[2];
127         }
128
129         /* normals for plane equations */
130         normal_tri_v3( clip[0],bb->vec[0], bb->vec[1], bb->vec[4]);
131         normal_tri_v3( clip[1],bb->vec[1], bb->vec[2], bb->vec[5]);
132         normal_tri_v3( clip[2],bb->vec[2], bb->vec[3], bb->vec[6]);
133         normal_tri_v3( clip[3],bb->vec[3], bb->vec[0], bb->vec[7]);
134         normal_tri_v3( clip[4],bb->vec[4], bb->vec[5], bb->vec[6]);
135         normal_tri_v3( clip[5],bb->vec[0], bb->vec[2], bb->vec[1]);
136
137         /* then plane equations */
138         for(val=0; val<5; val++) {
139                 clip[val][3]= - clip[val][0]*bb->vec[val][0] - clip[val][1]*bb->vec[val][1] - clip[val][2]*bb->vec[val][2];
140         }
141         clip[5][3]= - clip[5][0]*bb->vec[0][0] - clip[5][1]*bb->vec[0][1] - clip[5][2]*bb->vec[0][2];
142
143         /* create bounding box */
144         for(ar= sa->regionbase.first; ar; ar= ar->next) {
145                 if(ar->regiontype==RGN_TYPE_WINDOW) {
146                         RegionView3D *rv3d= ar->regiondata;
147
148                         if(rv3d->viewlock & RV3D_BOXCLIP) {
149                                 rv3d->rflag |= RV3D_CLIPPING;
150                                 memcpy(rv3d->clip, clip, sizeof(clip));
151                                 if(rv3d->clipbb) MEM_freeN(rv3d->clipbb);
152                                 rv3d->clipbb= MEM_dupallocN(bb);
153                         }
154                 }
155         }
156         MEM_freeN(bb);
157 }
158
159 /* sync center/zoom view of region to others, for view transforms */
160 static void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
161 {
162         ARegion *artest;
163         RegionView3D *rv3d= ar->regiondata;
164         short clip= 0;
165
166         for(artest= sa->regionbase.first; artest; artest= artest->next) {
167                 if(artest!=ar && artest->regiontype==RGN_TYPE_WINDOW) {
168                         RegionView3D *rv3dtest= artest->regiondata;
169
170                         if(rv3dtest->viewlock) {
171                                 rv3dtest->dist= rv3d->dist;
172
173                                 if( ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM) ) {
174                                         if( ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
175                                                 rv3dtest->ofs[0]= rv3d->ofs[0];
176                                         else if( ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
177                                                 rv3dtest->ofs[1]= rv3d->ofs[1];
178                                 }
179                                 else if( ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK) ) {
180                                         if( ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
181                                                 rv3dtest->ofs[0]= rv3d->ofs[0];
182                                         else if( ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
183                                                 rv3dtest->ofs[2]= rv3d->ofs[2];
184                                 }
185                                 else if( ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT) ) {
186                                         if( ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
187                                                 rv3dtest->ofs[1]= rv3d->ofs[1];
188                                         if( ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
189                                                 rv3dtest->ofs[2]= rv3d->ofs[2];
190                                 }
191
192                                 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
193
194                                 ED_region_tag_redraw(artest);
195                         }
196                 }
197         }
198
199         if(clip) {
200                 view3d_boxview_clip(sa);
201         }
202 }
203
204 /* for home, center etc */
205 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
206 {
207         ARegion *artest;
208         RegionView3D *rv3d= ar->regiondata;
209         short clip= 0;
210
211         for(artest= sa->regionbase.first; artest; artest= artest->next) {
212                 if(artest!=ar && artest->regiontype==RGN_TYPE_WINDOW) {
213                         RegionView3D *rv3dtest= artest->regiondata;
214
215                         if(rv3dtest->viewlock) {
216                                 rv3dtest->dist= rv3d->dist;
217                                 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
218                                 ED_region_tag_redraw(artest);
219
220                                 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
221                         }
222                 }
223         }
224
225         if(clip) {
226                 view3d_boxview_clip(sa);
227         }
228 }
229
230 /* 'clip' is used to know if our clip setting has changed */
231 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, short do_clip)
232 {
233         ARegion *arsync= NULL;
234         RegionView3D *rv3d= ar->regiondata;
235         short viewlock;
236         /* this function copies flags from the first of the 3 other quadview
237            regions to the 2 other, so it assumes this is the region whose
238            properties are always being edited, weak */
239         viewlock= rv3d->viewlock;
240
241         if((viewlock & RV3D_LOCKED)==0)
242                 viewlock= 0;
243         else if((viewlock & RV3D_BOXVIEW)==0) {
244                 viewlock &= ~RV3D_BOXCLIP;
245                 do_clip= TRUE;
246         }
247
248         for(; ar; ar= ar->prev) {
249                 if(ar->alignment==RGN_ALIGN_QSPLIT) {
250                         rv3d= ar->regiondata;
251                         rv3d->viewlock= viewlock;
252
253                         if(do_clip && (viewlock & RV3D_BOXCLIP)==0) {
254                                 rv3d->rflag &= ~RV3D_BOXCLIP;
255                         }
256
257                         /* use arsync so we sync with one of the aligned views below
258                          * else the view jumps on changing view settings like 'clip'
259                          * since it copies from the perspective view */
260                         arsync= ar;
261                 }
262         }
263
264         if(rv3d->viewlock & RV3D_BOXVIEW) {
265                 view3d_boxview_copy(sa, arsync ? arsync : sa->regionbase.last);
266         }
267
268         ED_area_tag_redraw(sa);
269 }
270
271 /* ************************** init for view ops **********************************/
272
273 typedef struct ViewOpsData {
274         ScrArea *sa;
275         ARegion *ar;
276         RegionView3D *rv3d;
277
278         /* needed for continuous zoom */
279         wmTimer *timer;
280         double timer_lastdraw;
281
282         float oldquat[4];
283         float trackvec[3];
284         float reverse, dist0;
285         float grid, far;
286         short axis_snap; /* view rotate only */
287
288         /* use for orbit selection and auto-dist */
289         float ofs[3], dyn_ofs[3];
290         short use_dyn_ofs;
291
292         int origx, origy, oldx, oldy;
293         int origkey; /* the key that triggered the operator */
294
295 } ViewOpsData;
296
297 #define TRACKBALLSIZE  (1.1)
298
299 static void calctrackballvec(rcti *rect, int mx, int my, float *vec)
300 {
301         float x, y, radius, d, z, t;
302
303         radius= TRACKBALLSIZE;
304
305         /* normalize x and y */
306         x= (rect->xmax + rect->xmin)/2 - mx;
307         x/= (float)((rect->xmax - rect->xmin)/4);
308         y= (rect->ymax + rect->ymin)/2 - my;
309         y/= (float)((rect->ymax - rect->ymin)/2);
310
311         d = sqrt(x*x + y*y);
312         if (d < radius*M_SQRT1_2)       /* Inside sphere */
313                 z = sqrt(radius*radius - d*d);
314         else
315         {                       /* On hyperbola */
316                 t = radius / M_SQRT2;
317                 z = t*t / d;
318         }
319
320         vec[0]= x;
321         vec[1]= y;
322         vec[2]= -z;             /* yah yah! */
323 }
324
325
326 static void viewops_data_create(bContext *C, wmOperator *op, wmEvent *event)
327 {
328         static float lastofs[3] = {0,0,0};
329         View3D *v3d;
330         RegionView3D *rv3d;
331         ViewOpsData *vod= MEM_callocN(sizeof(ViewOpsData), "viewops data");
332
333         /* store data */
334         op->customdata= vod;
335         vod->sa= CTX_wm_area(C);
336         vod->ar= CTX_wm_region(C);
337         v3d= vod->sa->spacedata.first;
338         vod->rv3d= rv3d= vod->ar->regiondata;
339         vod->dist0= rv3d->dist;
340         copy_qt_qt(vod->oldquat, rv3d->viewquat);
341         vod->origx= vod->oldx= event->x;
342         vod->origy= vod->oldy= event->y;
343         vod->origkey= event->type; /* the key that triggered the operator.  */
344         vod->use_dyn_ofs= (U.uiflag & USER_ORBIT_SELECTION) ? 1:0;
345
346         if (vod->use_dyn_ofs) {
347                 copy_v3_v3(vod->ofs, rv3d->ofs);
348                 /* If there's no selection, lastofs is unmodified and last value since static */
349                 calculateTransformCenter(C, V3D_CENTROID, lastofs);
350                 negate_v3_v3(vod->dyn_ofs, lastofs);
351         }
352         else if (U.uiflag & USER_ORBIT_ZBUF) {
353
354                 view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
355
356                 if((vod->use_dyn_ofs=view_autodist(CTX_data_scene(C), vod->ar, v3d, event->mval, vod->dyn_ofs))) {
357                         if (rv3d->persp==RV3D_PERSP) {
358                                 float my_origin[3]; /* original G.vd->ofs */
359                                 float my_pivot[3]; /* view */
360                                 float dvec[3];
361
362                                 // locals for dist correction
363                                 float mat[3][3];
364                                 float upvec[3];
365
366                                 negate_v3_v3(my_origin, rv3d->ofs);                             /* ofs is flipped */
367
368                                 /* Set the dist value to be the distance from this 3d point */
369                                 /* this means youll always be able to zoom into it and panning wont go bad when dist was zero */
370
371                                 /* remove dist value */
372                                 upvec[0] = upvec[1] = 0;
373                                 upvec[2] = rv3d->dist;
374                                 copy_m3_m4(mat, rv3d->viewinv);
375
376                                 mul_m3_v3(mat, upvec);
377                                 sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
378                                 negate_v3(my_pivot);                            /* ofs is flipped */
379
380                                 /* find a new ofs value that is allong the view axis (rather then the mouse location) */
381                                 closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
382                                 vod->dist0 = rv3d->dist = len_v3v3(my_pivot, dvec);
383
384                                 negate_v3_v3(rv3d->ofs, dvec);
385                         }
386                         negate_v3(vod->dyn_ofs);
387                         copy_v3_v3(vod->ofs, rv3d->ofs);
388                 } else {
389                         vod->ofs[0] = vod->ofs[1] = vod->ofs[2] = 0.0f;
390                 }
391         }
392
393         /* lookup, we dont pass on v3d to prevent confusement */
394         vod->grid= v3d->grid;
395         vod->far= v3d->far;
396
397         calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
398
399         initgrabz(rv3d, -rv3d->ofs[0], -rv3d->ofs[1], -rv3d->ofs[2]);
400
401         vod->reverse= 1.0f;
402         if (rv3d->persmat[2][1] < 0.0f)
403                 vod->reverse= -1.0f;
404
405         rv3d->rflag |= RV3D_NAVIGATING;
406 }
407
408 static void viewops_data_free(bContext *C, wmOperator *op)
409 {
410         ARegion *ar;
411         Paint *p = paint_get_active(CTX_data_scene(C));
412
413         if(op->customdata) {
414                 ViewOpsData *vod= op->customdata;
415                 ar= vod->ar;
416                 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
417
418                 if(vod->timer)
419                         WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
420
421                 MEM_freeN(vod);
422                 op->customdata= NULL;
423         }
424         else {
425                 ar= CTX_wm_region(C);
426         }
427
428         if(p && (p->flags & PAINT_FAST_NAVIGATE))
429                 ED_region_tag_redraw(ar);
430 }
431
432 /* ************************** viewrotate **********************************/
433
434 static const float thres = 0.93f; //cos(20 deg);
435
436 #define COS45 0.70710678118654746
437 #define SIN45 COS45
438
439 static float snapquats[39][5] = {
440         /*{q0, q1, q3, q4, view}*/
441         {COS45, -SIN45, 0.0, 0.0, RV3D_VIEW_FRONT},  //front
442         {0.0, 0.0, -SIN45, -SIN45, RV3D_VIEW_BACK}, //back
443         {1.0, 0.0, 0.0, 0.0, RV3D_VIEW_TOP},       //top
444         {0.0, -1.0, 0.0, 0.0, RV3D_VIEW_BOTTOM},      //bottom
445         {0.5, -0.5, -0.5, -0.5, RV3D_VIEW_RIGHT},    //left
446         {0.5, -0.5, 0.5, 0.5, RV3D_VIEW_LEFT},      //right
447
448         /* some more 45 deg snaps */
449         {0.65328145027160645, -0.65328145027160645, 0.27059805393218994, 0.27059805393218994, 0},
450         {0.92387950420379639, 0.0, 0.0, 0.38268342614173889, 0},
451         {0.0, -0.92387950420379639, 0.38268342614173889, 0.0, 0},
452         {0.35355335474014282, -0.85355335474014282, 0.35355338454246521, 0.14644660055637360, 0},
453         {0.85355335474014282, -0.35355335474014282, 0.14644660055637360, 0.35355338454246521, 0},
454         {0.49999994039535522, -0.49999994039535522, 0.49999997019767761, 0.49999997019767761, 0},
455         {0.27059802412986755, -0.65328145027160645, 0.65328145027160645, 0.27059802412986755, 0},
456         {0.65328145027160645, -0.27059802412986755, 0.27059802412986755, 0.65328145027160645, 0},
457         {0.27059799432754517, -0.27059799432754517, 0.65328139066696167, 0.65328139066696167, 0},
458         {0.38268336653709412, 0.0, 0.0, 0.92387944459915161, 0},
459         {0.0, -0.38268336653709412, 0.92387944459915161, 0.0, 0},
460         {0.14644658565521240, -0.35355335474014282, 0.85355335474014282, 0.35355335474014282, 0},
461         {0.35355335474014282, -0.14644658565521240, 0.35355335474014282, 0.85355335474014282, 0},
462         {0.0, 0.0, 0.92387944459915161, 0.38268336653709412, 0},
463         {-0.0, 0.0, 0.38268336653709412, 0.92387944459915161, 0},
464         {-0.27059802412986755, 0.27059802412986755, 0.65328133106231689, 0.65328133106231689, 0},
465         {-0.38268339633941650, 0.0, 0.0, 0.92387938499450684, 0},
466         {0.0, 0.38268339633941650, 0.92387938499450684, 0.0, 0},
467         {-0.14644658565521240, 0.35355338454246521, 0.85355329513549805, 0.35355332493782043, 0},
468         {-0.35355338454246521, 0.14644658565521240, 0.35355332493782043, 0.85355329513549805, 0},
469         {-0.49999991059303284, 0.49999991059303284, 0.49999985098838806, 0.49999985098838806, 0},
470         {-0.27059799432754517, 0.65328145027160645, 0.65328139066696167, 0.27059799432754517, 0},
471         {-0.65328145027160645, 0.27059799432754517, 0.27059799432754517, 0.65328139066696167, 0},
472         {-0.65328133106231689, 0.65328133106231689, 0.27059793472290039, 0.27059793472290039, 0},
473         {-0.92387932538986206, 0.0, 0.0, 0.38268333673477173, 0},
474         {0.0, 0.92387932538986206, 0.38268333673477173, 0.0, 0},
475         {-0.35355329513549805, 0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0},
476         {-0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0.35355329513549805, 0},
477         {-0.38268330693244934, 0.92387938499450684, 0.0, 0.0, 0},
478         {-0.92387938499450684, 0.38268330693244934, 0.0, 0.0, 0},
479         {-COS45, 0.0, 0.0, SIN45, 0},
480         {COS45, 0.0, 0.0, SIN45, 0},
481         {0.0, 0.0, 0.0, 1.0, 0}
482 };
483
484 enum {
485         VIEW_PASS= 0,
486         VIEW_APPLY,
487         VIEW_CONFIRM
488 };
489
490 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
491 #define VIEW_MODAL_CONFIRM                              1 /* used for all view operations */
492 #define VIEWROT_MODAL_AXIS_SNAP_ENABLE  2
493 #define VIEWROT_MODAL_AXIS_SNAP_DISABLE 3
494 #define VIEWROT_MODAL_SWITCH_ZOOM               4
495 #define VIEWROT_MODAL_SWITCH_MOVE               5
496 #define VIEWROT_MODAL_SWITCH_ROTATE             6
497
498 /* called in transform_ops.c, on each regeneration of keymaps  */
499 void viewrotate_modal_keymap(wmKeyConfig *keyconf)
500 {
501         static EnumPropertyItem modal_items[] = {
502         {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
503
504         {VIEWROT_MODAL_AXIS_SNAP_ENABLE,        "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
505         {VIEWROT_MODAL_AXIS_SNAP_DISABLE,       "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
506                 
507         {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
508         {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
509
510         {0, NULL, 0, NULL, NULL}};
511
512         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Rotate Modal");
513
514         /* this function is called for each spacetype, only needs to add map once */
515         if(keymap) return;
516
517         keymap= WM_modalkeymap_add(keyconf, "View3D Rotate Modal", modal_items);
518
519         /* items for modal map */
520         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
521         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
522
523         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_ENABLE);
524         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_DISABLE);
525
526         /* disabled mode switching for now, can re-implement better, later on
527         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
528         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
529         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
530         */
531         
532         /* assign map to operators */
533         WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
534
535 }
536
537 static void viewrotate_apply(ViewOpsData *vod, int x, int y)
538 {
539         RegionView3D *rv3d= vod->rv3d;
540
541         rv3d->view= 0; /* need to reset everytime because of view snapping */
542
543         if (U.flag & USER_TRACKBALL) {
544                 float phi, si, q1[4], dvec[3], newvec[3];
545
546                 calctrackballvec(&vod->ar->winrct, x, y, newvec);
547
548                 sub_v3_v3v3(dvec, newvec, vod->trackvec);
549
550                 si= sqrt(dvec[0]*dvec[0]+ dvec[1]*dvec[1]+ dvec[2]*dvec[2]);
551                 si/= (2.0*TRACKBALLSIZE);
552
553                 cross_v3_v3v3(q1+1, vod->trackvec, newvec);
554                 normalize_v3(q1+1);
555
556                 /* Allow for rotation beyond the interval
557                         * [-pi, pi] */
558                 while (si > 1.0)
559                         si -= 2.0;
560
561                 /* This relation is used instead of
562                         * phi = asin(si) so that the angle
563                         * of rotation is linearly proportional
564                         * to the distance that the mouse is
565                         * dragged. */
566                 phi = si * M_PI / 2.0;
567
568                 q1[0]= cos(phi);
569                 mul_v3_fl(q1+1, sin(phi));
570                 mul_qt_qtqt(rv3d->viewquat, q1, vod->oldquat);
571
572                 if (vod->use_dyn_ofs) {
573                         /* compute the post multiplication quat, to rotate the offset correctly */
574                         copy_qt_qt(q1, vod->oldquat);
575                         conjugate_qt(q1);
576                         mul_qt_qtqt(q1, q1, rv3d->viewquat);
577
578                         conjugate_qt(q1); /* conj == inv for unit quat */
579                         copy_v3_v3(rv3d->ofs, vod->ofs);
580                         sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
581                         mul_qt_v3(q1, rv3d->ofs);
582                         add_v3_v3(rv3d->ofs, vod->dyn_ofs);
583                 }
584         }
585         else {
586                 /* New turntable view code by John Aughey */
587                 float phi, q1[4];
588                 float m[3][3];
589                 float m_inv[3][3];
590                 float xvec[3] = {1,0,0};
591                 /* Sensitivity will control how fast the viewport rotates.  0.0035 was
592                         obtained experimentally by looking at viewport rotation sensitivities
593                         on other modeling programs. */
594                 /* Perhaps this should be a configurable user parameter. */
595                 const float sensitivity = 0.0035;
596
597                 /* Get the 3x3 matrix and its inverse from the quaternion */
598                 quat_to_mat3( m,rv3d->viewquat);
599                 invert_m3_m3(m_inv,m);
600
601                 /* Determine the direction of the x vector (for rotating up and down) */
602                 /* This can likely be computed directly from the quaternion. */
603                 mul_m3_v3(m_inv,xvec);
604
605                 /* Perform the up/down rotation */
606                 phi = sensitivity * -(y - vod->oldy);
607                 q1[0] = cos(phi);
608                 mul_v3_v3fl(q1+1, xvec, sin(phi));
609                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
610
611                 if (vod->use_dyn_ofs) {
612                         conjugate_qt(q1); /* conj == inv for unit quat */
613                         sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
614                         mul_qt_v3(q1, rv3d->ofs);
615                         add_v3_v3(rv3d->ofs, vod->dyn_ofs);
616                 }
617
618                 /* Perform the orbital rotation */
619                 phi = sensitivity * vod->reverse * (x - vod->oldx);
620                 q1[0] = cos(phi);
621                 q1[1] = q1[2] = 0.0;
622                 q1[3] = sin(phi);
623                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
624
625                 if (vod->use_dyn_ofs) {
626                         conjugate_qt(q1);
627                         sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
628                         mul_qt_v3(q1, rv3d->ofs);
629                         add_v3_v3(rv3d->ofs, vod->dyn_ofs);
630                 }
631         }
632
633         /* check for view snap */
634         if (vod->axis_snap){
635                 int i;
636                 float viewquat_inv[4];
637                 float zaxis[3]={0,0,1};
638                 invert_qt_qt(viewquat_inv, rv3d->viewquat);
639
640                 mul_qt_v3(viewquat_inv, zaxis);
641
642                 for (i = 0 ; i < 39; i++){
643
644                         float view = (int)snapquats[i][4];
645                         float viewquat_inv_test[4];
646                         float zaxis_test[3]={0,0,1};
647
648                         invert_qt_qt(viewquat_inv_test, snapquats[i]);
649                         mul_qt_v3(viewquat_inv_test, zaxis_test);
650                         
651                         if(angle_v3v3(zaxis_test, zaxis) < DEG2RAD(45/3)) {
652                                 /* find the best roll */
653                                 float quat_roll[4], quat_final[4], quat_best[4];
654                                 float viewquat_align[4]; /* viewquat aligned to zaxis_test */
655                                 float viewquat_align_inv[4]; /* viewquat aligned to zaxis_test */
656                                 float best_angle = FLT_MAX;
657                                 int j;
658
659                                 /* viewquat_align is the original viewquat aligned to the snapped axis
660                                  * for testing roll */
661                                 rotation_between_vecs_to_quat(viewquat_align, zaxis_test, zaxis);
662                                 normalize_qt(viewquat_align);
663                                 mul_qt_qtqt(viewquat_align, rv3d->viewquat, viewquat_align);
664                                 normalize_qt(viewquat_align);
665                                 invert_qt_qt(viewquat_align_inv, viewquat_align);
666
667                                 /* find best roll */
668                                 for(j= 0; j<8; j++) {
669                                         float angle;
670                                         float xaxis1[3]={1,0,0};
671                                         float xaxis2[3]={1,0,0};
672                                         float quat_final_inv[4];
673
674                                         axis_angle_to_quat(quat_roll, zaxis_test, j * DEG2RAD(45.0));
675                                         normalize_qt(quat_roll);
676
677                                         mul_qt_qtqt(quat_final, snapquats[i], quat_roll);
678                                         normalize_qt(quat_final);
679                                         
680                                         /* compare 2 vector angles to find the least roll */
681                                         invert_qt_qt(quat_final_inv, quat_final);
682                                         mul_qt_v3(viewquat_align_inv, xaxis1);
683                                         mul_qt_v3(quat_final_inv, xaxis2);
684                                         angle= angle_v3v3(xaxis1, xaxis2);
685
686                                         if(angle <= best_angle) {
687                                                 best_angle= angle;
688                                                 copy_qt_qt(quat_best, quat_final);
689                                                 if(j) view= 0; /* view grid assumes certain up axis */
690                                         }
691                                 }
692
693                                 copy_qt_qt(rv3d->viewquat, quat_best);
694                                 rv3d->view= view; /* if we snap to a rolled camera the grid is invalid */
695
696                                 break;
697                         }
698                 }
699         }
700         vod->oldx= x;
701         vod->oldy= y;
702
703         /* avoid precision loss over time */
704         normalize_qt(rv3d->viewquat);
705
706         ED_region_tag_redraw(vod->ar);
707 }
708
709 static int viewrotate_modal(bContext *C, wmOperator *op, wmEvent *event)
710 {
711         ViewOpsData *vod= op->customdata;
712         short event_code= VIEW_PASS;
713
714         /* execute the events */
715         if(event->type==MOUSEMOVE) {
716                 event_code= VIEW_APPLY;
717         }
718         else if(event->type==EVT_MODAL_MAP) {
719                 switch (event->val) {
720                         case VIEW_MODAL_CONFIRM:
721                                 event_code= VIEW_CONFIRM;
722                                 break;
723                         case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
724                                 vod->axis_snap= TRUE;
725                                 event_code= VIEW_APPLY;
726                                 break;
727                         case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
728                                 vod->axis_snap= FALSE;
729                                 event_code= VIEW_APPLY;
730                                 break;
731                         case VIEWROT_MODAL_SWITCH_ZOOM:
732                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
733                                 event_code= VIEW_CONFIRM;
734                                 break;
735                         case VIEWROT_MODAL_SWITCH_MOVE:
736                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
737                                 event_code= VIEW_CONFIRM;
738                                 break;
739                 }
740         }
741         else if(event->type==vod->origkey && event->val==KM_RELEASE) {
742                 event_code= VIEW_CONFIRM;
743         }
744
745         if(event_code==VIEW_APPLY) {
746                 viewrotate_apply(vod, event->x, event->y);
747         }
748         else if (event_code==VIEW_CONFIRM) {
749                 request_depth_update(vod->rv3d);
750                 viewops_data_free(C, op);
751
752                 return OPERATOR_FINISHED;
753         }
754
755         return OPERATOR_RUNNING_MODAL;
756 }
757
758 static int viewrotate_invoke(bContext *C, wmOperator *op, wmEvent *event)
759 {
760         ViewOpsData *vod;
761         RegionView3D *rv3d;
762
763         /* makes op->customdata */
764         viewops_data_create(C, op, event);
765         vod= op->customdata;
766         rv3d= vod->rv3d;
767
768         if(rv3d->viewlock) { /* poll should check but in some cases fails, see poll func for details */
769                 viewops_data_free(C, op);
770                 return OPERATOR_PASS_THROUGH;
771         }
772
773         /* switch from camera view when: */
774         if(rv3d->persp != RV3D_PERSP) {
775
776                 if (U.uiflag & USER_AUTOPERSP) {
777                         rv3d->persp= RV3D_PERSP;
778                 }
779                 else if(rv3d->persp==RV3D_CAMOB) {
780
781                         /* changed since 2.4x, use the camera view */
782                         View3D *v3d = vod->sa->spacedata.first;
783
784                         if(v3d->camera) {
785                                 view3d_settings_from_ob(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
786                         }
787
788                         if(rv3d->persp==RV3D_CAMOB) {
789                                 rv3d->persp= rv3d->lpersp;
790                         }
791                 }
792                 ED_region_tag_redraw(vod->ar);
793         }
794         
795         if (event->type == MOUSEPAN) {
796                 viewrotate_apply(vod, event->prevx, event->prevy);
797                 request_depth_update(rv3d);
798                 
799                 viewops_data_free(C, op);
800                 
801                 return OPERATOR_FINISHED;
802         }
803         else if (event->type == MOUSEROTATE) {
804                 /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
805                 viewrotate_apply(vod, event->prevx, event->y);
806                 request_depth_update(rv3d);
807                 
808                 viewops_data_free(C, op);
809                 
810                 return OPERATOR_FINISHED;
811         }
812         else {          
813                 /* add temp handler */
814                 WM_event_add_modal_handler(C, op);
815
816                 return OPERATOR_RUNNING_MODAL;
817         }
818 }
819
820 static int view3d_camera_active_poll(bContext *C)
821 {
822         if(ED_operator_view3d_active(C)) {
823                 RegionView3D *rv3d= CTX_wm_region_view3d(C);
824                 if(rv3d && rv3d->persp==RV3D_CAMOB) {
825                         return 1;
826                 }
827         }
828
829         return 0;
830 }
831
832 void VIEW3D_OT_rotate(wmOperatorType *ot)
833 {
834
835         /* identifiers */
836         ot->name= "Rotate view";
837         ot->description = "Rotate the view";
838         ot->idname= "VIEW3D_OT_rotate";
839
840         /* api callbacks */
841         ot->invoke= viewrotate_invoke;
842         ot->modal= viewrotate_modal;
843         ot->poll= ED_operator_region_view3d_active;
844
845         /* flags */
846         ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
847 }
848
849 /* ************************ viewmove ******************************** */
850
851
852 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
853
854 /* called in transform_ops.c, on each regeneration of keymaps  */
855 void viewmove_modal_keymap(wmKeyConfig *keyconf)
856 {
857         static EnumPropertyItem modal_items[] = {
858         {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
859                 
860         {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
861         {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
862
863         {0, NULL, 0, NULL, NULL}};
864
865         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Move Modal");
866
867         /* this function is called for each spacetype, only needs to add map once */
868         if(keymap) return;
869
870         keymap= WM_modalkeymap_add(keyconf, "View3D Move Modal", modal_items);
871
872         /* items for modal map */
873         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
874         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
875
876         /* disabled mode switching for now, can re-implement better, later on
877         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
878         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
879         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
880         */
881         
882         /* assign map to operators */
883         WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
884 }
885
886
887 static void viewmove_apply(ViewOpsData *vod, int x, int y)
888 {
889         if(vod->rv3d->persp==RV3D_CAMOB) {
890                 float zoomfac= (M_SQRT2 + vod->rv3d->camzoom/50.0);
891                 zoomfac= (zoomfac*zoomfac)*0.5;
892
893                 vod->rv3d->camdx += (vod->oldx - x)/(vod->ar->winx * zoomfac);
894                 vod->rv3d->camdy += (vod->oldy - y)/(vod->ar->winy * zoomfac);
895                 CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
896                 CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
897 // XXX          preview3d_event= 0;
898         }
899         else {
900                 float dvec[3];
901
902                 window_to_3d_delta(vod->ar, dvec, x-vod->oldx, y-vod->oldy);
903                 add_v3_v3(vod->rv3d->ofs, dvec);
904
905                 if(vod->rv3d->viewlock & RV3D_BOXVIEW)
906                         view3d_boxview_sync(vod->sa, vod->ar);
907         }
908
909         vod->oldx= x;
910         vod->oldy= y;
911
912         ED_region_tag_redraw(vod->ar);
913 }
914
915
916 static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
917 {
918
919         ViewOpsData *vod= op->customdata;
920         short event_code= VIEW_PASS;
921
922         /* execute the events */
923         if(event->type==MOUSEMOVE) {
924                 event_code= VIEW_APPLY;
925         }
926         else if(event->type==EVT_MODAL_MAP) {
927                 switch (event->val) {
928                         case VIEW_MODAL_CONFIRM:
929                                 event_code= VIEW_CONFIRM;
930                                 break;
931                         case VIEWROT_MODAL_SWITCH_ZOOM:
932                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
933                                 event_code= VIEW_CONFIRM;
934                                 break;
935                         case VIEWROT_MODAL_SWITCH_ROTATE:
936                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
937                                 event_code= VIEW_CONFIRM;
938                                 break;
939                 }
940         }
941         else if(event->type==vod->origkey && event->val==KM_RELEASE) {
942                 event_code= VIEW_CONFIRM;
943         }
944
945         if(event_code==VIEW_APPLY) {
946                 viewmove_apply(vod, event->x, event->y);
947         }
948         else if (event_code==VIEW_CONFIRM) {
949                 request_depth_update(vod->rv3d);
950
951                 viewops_data_free(C, op);
952
953                 return OPERATOR_FINISHED;
954         }
955
956         return OPERATOR_RUNNING_MODAL;
957 }
958
959 static int viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
960 {
961         /* makes op->customdata */
962         viewops_data_create(C, op, event);
963
964         if (event->type == MOUSEPAN) {
965                 ViewOpsData *vod= op->customdata;
966                 viewmove_apply(vod, event->prevx, event->prevy);
967                 request_depth_update(vod->rv3d);
968                 
969                 viewops_data_free(C, op);               
970                 
971                 return OPERATOR_FINISHED;
972         }
973         else {
974                 /* add temp handler */
975                 WM_event_add_modal_handler(C, op);
976
977                 return OPERATOR_RUNNING_MODAL;
978         }
979 }
980
981 void VIEW3D_OT_move(wmOperatorType *ot)
982 {
983
984         /* identifiers */
985         ot->name= "Move view";
986         ot->description = "Move the view";
987         ot->idname= "VIEW3D_OT_move";
988
989         /* api callbacks */
990         ot->invoke= viewmove_invoke;
991         ot->modal= viewmove_modal;
992         ot->poll= ED_operator_view3d_active;
993
994         /* flags */
995         ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
996 }
997
998 /* ************************ viewzoom ******************************** */
999
1000 /* called in transform_ops.c, on each regeneration of keymaps  */
1001 void viewzoom_modal_keymap(wmKeyConfig *keyconf)
1002 {
1003         static EnumPropertyItem modal_items[] = {
1004         {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
1005                 
1006         {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1007         {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
1008
1009         {0, NULL, 0, NULL, NULL}};
1010
1011         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Zoom Modal");
1012
1013         /* this function is called for each spacetype, only needs to add map once */
1014         if(keymap) return;
1015
1016         keymap= WM_modalkeymap_add(keyconf, "View3D Zoom Modal", modal_items);
1017
1018         /* items for modal map */
1019         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1020         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1021
1022         /* disabled mode switching for now, can re-implement better, later on
1023         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1024         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1025         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
1026          */
1027         
1028         /* assign map to operators */
1029         WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
1030 }
1031
1032 static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my)
1033 {
1034         RegionView3D *rv3d= ar->regiondata;
1035
1036         if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1037                 float dvec[3];
1038                 float tvec[3];
1039                 float tpos[3];
1040                 float new_dist;
1041                 short vb[2], mouseloc[2];
1042
1043                 mouseloc[0]= mx - ar->winrct.xmin;
1044                 mouseloc[1]= my - ar->winrct.ymin;
1045
1046                 /* find the current window width and height */
1047                 vb[0] = ar->winx;
1048                 vb[1] = ar->winy;
1049
1050                 negate_v3_v3(tpos, rv3d->ofs);
1051
1052                 /* Project cursor position into 3D space */
1053                 initgrabz(rv3d, tpos[0], tpos[1], tpos[2]);
1054                 window_to_3d_delta(ar, dvec, mouseloc[0]-vb[0]/2, mouseloc[1]-vb[1]/2);
1055
1056                 /* Calculate view target position for dolly */
1057                 add_v3_v3v3(tvec, tpos, dvec);
1058                 negate_v3(tvec);
1059
1060                 /* Offset to target position and dolly */
1061                 new_dist = rv3d->dist * dfac;
1062
1063                 copy_v3_v3(rv3d->ofs, tvec);
1064                 rv3d->dist = new_dist;
1065
1066                 /* Calculate final offset */
1067                 madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
1068         } else {
1069                 rv3d->dist *= dfac;
1070         }
1071 }
1072
1073
1074 static void viewzoom_apply(ViewOpsData *vod, int x, int y, short viewzoom)
1075 {
1076         float zfac=1.0;
1077
1078         if(viewzoom==USER_ZOOM_CONT) {
1079                 double time= PIL_check_seconds_timer();
1080                 float time_step= (float)(time - vod->timer_lastdraw);
1081
1082                 // oldstyle zoom
1083                 zfac = 1.0f + (((float)(vod->origx - x + vod->origy - y)/20.0) * time_step);
1084                 vod->timer_lastdraw= time;
1085         }
1086         else if(viewzoom==USER_ZOOM_SCALE) {
1087                 int ctr[2], len1, len2;
1088                 // method which zooms based on how far you move the mouse
1089
1090                 ctr[0] = (vod->ar->winrct.xmax + vod->ar->winrct.xmin)/2;
1091                 ctr[1] = (vod->ar->winrct.ymax + vod->ar->winrct.ymin)/2;
1092
1093                 len1 = (int)sqrt((ctr[0] - x)*(ctr[0] - x) + (ctr[1] - y)*(ctr[1] - y)) + 5;
1094                 len2 = (int)sqrt((ctr[0] - vod->origx)*(ctr[0] - vod->origx) + (ctr[1] - vod->origy)*(ctr[1] - vod->origy)) + 5;
1095
1096                 zfac = vod->dist0 * ((float)len2/len1) / vod->rv3d->dist;
1097         }
1098         else {  /* USER_ZOOM_DOLLY */
1099                 float len1, len2;
1100                 
1101                 if (U.uiflag & USER_ZOOM_DOLLY_HORIZ) {
1102                         len1 = (vod->ar->winrct.xmax - x) + 5;
1103                         len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
1104                 }
1105                 else {
1106                         len1 = (vod->ar->winrct.ymax - y) + 5;
1107                         len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
1108                 }
1109                 if (U.uiflag & USER_ZOOM_INVERT)
1110                         SWAP(float, len1, len2);
1111                 
1112                 zfac = vod->dist0 * (2.0*((len2/len1)-1.0) + 1.0) / vod->rv3d->dist;
1113         }
1114
1115         if(zfac != 1.0 && zfac*vod->rv3d->dist > 0.001*vod->grid &&
1116                                 zfac*vod->rv3d->dist < 10.0*vod->far)
1117                 view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy);
1118
1119
1120         if ((U.uiflag & USER_ORBIT_ZBUF) && (viewzoom==USER_ZOOM_CONT) && (vod->rv3d->persp==RV3D_PERSP)) {
1121                 float upvec[3], mat[3][3];
1122
1123                 /* Secret apricot feature, translate the view when in continues mode */
1124                 upvec[0] = upvec[1] = 0.0f;
1125                 upvec[2] = (vod->dist0 - vod->rv3d->dist) * vod->grid;
1126                 vod->rv3d->dist = vod->dist0;
1127                 copy_m3_m4(mat, vod->rv3d->viewinv);
1128                 mul_m3_v3(mat, upvec);
1129                 add_v3_v3(vod->rv3d->ofs, upvec);
1130         } else {
1131                 /* these limits were in old code too */
1132                 if(vod->rv3d->dist<0.001*vod->grid) vod->rv3d->dist= 0.001*vod->grid;
1133                 if(vod->rv3d->dist>10.0*vod->far) vod->rv3d->dist=10.0*vod->far;
1134         }
1135
1136 // XXX  if(vod->rv3d->persp==RV3D_ORTHO || vod->rv3d->persp==RV3D_CAMOB) preview3d_event= 0;
1137
1138         if(vod->rv3d->viewlock & RV3D_BOXVIEW)
1139                 view3d_boxview_sync(vod->sa, vod->ar);
1140
1141         ED_region_tag_redraw(vod->ar);
1142 }
1143
1144
1145 static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event)
1146 {
1147         ViewOpsData *vod= op->customdata;
1148         short event_code= VIEW_PASS;
1149
1150         /* execute the events */
1151         if (event->type == TIMER && event->customdata == vod->timer) {
1152                 /* continuous zoom */
1153                 event_code= VIEW_APPLY;
1154         }
1155         else if(event->type==MOUSEMOVE) {
1156                 event_code= VIEW_APPLY;
1157         }
1158         else if(event->type==EVT_MODAL_MAP) {
1159                 switch (event->val) {
1160                         case VIEW_MODAL_CONFIRM:
1161                                 event_code= VIEW_CONFIRM;
1162                                 break;
1163                         case VIEWROT_MODAL_SWITCH_MOVE:
1164                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1165                                 event_code= VIEW_CONFIRM;
1166                                 break;
1167                         case VIEWROT_MODAL_SWITCH_ROTATE:
1168                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1169                                 event_code= VIEW_CONFIRM;
1170                                 break;
1171                 }
1172         }
1173         else if(event->type==vod->origkey && event->val==KM_RELEASE) {
1174                 event_code= VIEW_CONFIRM;
1175         }
1176
1177         if(event_code==VIEW_APPLY) {
1178                 viewzoom_apply(vod, event->x, event->y, U.viewzoom);
1179         }
1180         else if (event_code==VIEW_CONFIRM) {
1181                 request_depth_update(vod->rv3d);
1182                 viewops_data_free(C, op);
1183
1184                 return OPERATOR_FINISHED;
1185         }
1186
1187         return OPERATOR_RUNNING_MODAL;
1188 }
1189
1190 static int viewzoom_exec(bContext *C, wmOperator *op)
1191 {
1192         View3D *v3d;
1193         RegionView3D *rv3d;
1194         ScrArea *sa;
1195         ARegion *ar;
1196
1197         int delta= RNA_int_get(op->ptr, "delta");
1198         int mx, my;
1199
1200         if(op->customdata) {
1201                 ViewOpsData *vod= op->customdata;
1202
1203                 sa= vod->sa;
1204                 ar= vod->ar;
1205         }
1206         else {
1207                 sa= CTX_wm_area(C);
1208                 ar= CTX_wm_region(C);
1209         }
1210
1211         v3d= sa->spacedata.first;
1212         rv3d= ar->regiondata;
1213
1214         mx= RNA_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : ar->winx / 2;
1215         my= RNA_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : ar->winy / 2;
1216
1217         if(delta < 0) {
1218                 /* this min and max is also in viewmove() */
1219                 if(rv3d->persp==RV3D_CAMOB) {
1220                         rv3d->camzoom-= 10;
1221                         if(rv3d->camzoom < RV3D_CAMZOOM_MIN) rv3d->camzoom= RV3D_CAMZOOM_MIN;
1222                 }
1223                 else if(rv3d->dist<10.0*v3d->far) {
1224                         view_zoom_mouseloc(ar, 1.2f, mx, my);
1225                 }
1226         }
1227         else {
1228                 if(rv3d->persp==RV3D_CAMOB) {
1229                         rv3d->camzoom+= 10;
1230                         if(rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom= RV3D_CAMZOOM_MAX;
1231                 }
1232                 else if(rv3d->dist> 0.001*v3d->grid) {
1233                         view_zoom_mouseloc(ar, .83333f, mx, my);
1234                 }
1235         }
1236
1237         if(rv3d->viewlock & RV3D_BOXVIEW)
1238                 view3d_boxview_sync(sa, ar);
1239
1240         request_depth_update(rv3d);
1241         ED_region_tag_redraw(ar);
1242
1243         viewops_data_free(C, op);
1244
1245         return OPERATOR_FINISHED;
1246 }
1247
1248 static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
1249 {
1250         /* if one or the other zoom position aren't set, set from event */
1251         if (!RNA_property_is_set(op->ptr, "mx") || !RNA_property_is_set(op->ptr, "my"))
1252         {
1253                 RNA_int_set(op->ptr, "mx", event->x);
1254                 RNA_int_set(op->ptr, "my", event->y);
1255         }
1256
1257         if(RNA_property_is_set(op->ptr, "delta")) {
1258                 /* makes op->customdata */
1259                 viewops_data_create(C, op, event);
1260                 viewzoom_exec(C, op);
1261         }
1262         else {
1263                 ViewOpsData *vod;
1264
1265                 /* makes op->customdata */
1266                 viewops_data_create(C, op, event);
1267
1268                 vod= op->customdata;
1269
1270                 if (event->type == MOUSEZOOM) {
1271                         if (U.uiflag & USER_ZOOM_INVERT) /* Bypass Zoom invert flag */
1272                                 SWAP(int, event->x, event->prevx);
1273
1274                         if (U.uiflag & USER_ZOOM_DOLLY_HORIZ) {
1275                                 vod->origx = vod->oldx = event->x;
1276                                 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY);
1277                         }
1278                         else {
1279                                 
1280                                 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
1281                                 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
1282                                 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY);
1283                         }
1284                         request_depth_update(vod->rv3d);
1285                         
1286                         viewops_data_free(C, op);
1287                         return OPERATOR_FINISHED;
1288                 }
1289                 else {
1290                         if(U.viewzoom == USER_ZOOM_CONT) {
1291                                 /* needs a timer to continue redrawing */
1292                                 vod->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
1293                                 vod->timer_lastdraw= PIL_check_seconds_timer();
1294                         }
1295
1296                         /* add temp handler */
1297                         WM_event_add_modal_handler(C, op);
1298
1299                         return OPERATOR_RUNNING_MODAL;
1300                 }
1301         }
1302         return OPERATOR_FINISHED;
1303 }
1304
1305
1306 void VIEW3D_OT_zoom(wmOperatorType *ot)
1307 {
1308         /* identifiers */
1309         ot->name= "Zoom view";
1310         ot->description = "Zoom in/out in the view";
1311         ot->idname= "VIEW3D_OT_zoom";
1312
1313         /* api callbacks */
1314         ot->invoke= viewzoom_invoke;
1315         ot->exec= viewzoom_exec;
1316         ot->modal= viewzoom_modal;
1317         ot->poll= ED_operator_region_view3d_active;
1318
1319         /* flags */
1320         ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
1321
1322         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1323         RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
1324         RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
1325 }
1326
1327 static int view3d_all_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
1328 {
1329         ARegion *ar= CTX_wm_region(C);
1330         View3D *v3d = CTX_wm_view3d(C);
1331         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1332         Scene *scene= CTX_data_scene(C);
1333         Base *base;
1334         float *curs;
1335
1336         int center= RNA_boolean_get(op->ptr, "center");
1337
1338         float size, min[3], max[3], afm[3];
1339         int ok= 1, onedone=0;
1340
1341         if(center) {
1342                 /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
1343                 curs= give_cursor(scene, v3d);
1344                 zero_v3(min);
1345                 zero_v3(max);
1346                 zero_v3(curs);
1347         }
1348         else {
1349                 INIT_MINMAX(min, max);
1350         }
1351
1352         for(base= scene->base.first; base; base= base->next) {
1353                 if(BASE_VISIBLE(v3d, base)) {
1354                         onedone= 1;
1355                         minmax_object(base->object, min, max);
1356                 }
1357         }
1358         if(!onedone) {
1359                 ED_region_tag_redraw(ar);
1360                 /* TODO - should this be cancel?
1361                  * I think no, because we always move the cursor, with or without
1362                  * object, but in this case there is no change in the scene,
1363                  * only the cursor so I choice a ED_region_tag like
1364                  * smooth_view do for the center_cursor.
1365                  * See bug #22640
1366                  */
1367                 return OPERATOR_FINISHED;
1368         }
1369
1370         sub_v3_v3v3(afm, max, min);
1371         size= 0.7f*MAX3(afm[0], afm[1], afm[2]);
1372         if(size==0.0) ok= 0;
1373
1374         if(ok) {
1375                 float new_dist;
1376                 float new_ofs[3];
1377
1378                 new_dist = size;
1379                 new_ofs[0]= -(min[0]+max[0])/2.0f;
1380                 new_ofs[1]= -(min[1]+max[1])/2.0f;
1381                 new_ofs[2]= -(min[2]+max[2])/2.0f;
1382
1383                 // correction for window aspect ratio
1384                 if(ar->winy>2 && ar->winx>2) {
1385                         size= (float)ar->winx/(float)ar->winy;
1386                         if(size<1.0) size= 1.0f/size;
1387                         new_dist*= size;
1388                 }
1389
1390                 if (rv3d->persp==RV3D_CAMOB) {
1391                         rv3d->persp= RV3D_PERSP;
1392                         smooth_view(C, v3d->camera, NULL, new_ofs, NULL, &new_dist, NULL);
1393                 }
1394                 else {
1395                         smooth_view(C, NULL, NULL, new_ofs, NULL, &new_dist, NULL);
1396                 }
1397         }
1398 // XXX  BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1399
1400         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
1401
1402         return OPERATOR_FINISHED;
1403 }
1404
1405
1406 void VIEW3D_OT_view_all(wmOperatorType *ot)
1407 {
1408         /* identifiers */
1409         ot->name= "View All";
1410         ot->description = "View all objects in scene";
1411         ot->idname= "VIEW3D_OT_view_all";
1412
1413         /* api callbacks */
1414         ot->exec= view3d_all_exec;
1415         ot->poll= ED_operator_region_view3d_active;
1416
1417         /* flags */
1418         ot->flag= 0;
1419
1420         RNA_def_boolean(ot->srna, "center", 0, "Center", "");
1421 }
1422
1423
1424 static int viewselected_exec(bContext *C, wmOperator *UNUSED(op)) /* like a localview without local!, was centerview() in 2.4x */
1425 {
1426         ARegion *ar= CTX_wm_region(C);
1427         View3D *v3d = CTX_wm_view3d(C);
1428         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1429         Scene *scene= CTX_data_scene(C);
1430         Object *ob= OBACT;
1431         Object *obedit= CTX_data_edit_object(C);
1432         float size, min[3], max[3], afm[3];
1433         int ok=0, ok_dist=1;
1434
1435         /* SMOOTHVIEW */
1436         float new_ofs[3];
1437         float new_dist;
1438
1439         INIT_MINMAX(min, max);
1440
1441         if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
1442                 /* hardcoded exception, we look for the one selected armature */
1443                 /* this is weak code this way, we should make a generic active/selection callback interface once... */
1444                 Base *base;
1445                 for(base=scene->base.first; base; base= base->next) {
1446                         if(TESTBASELIB(v3d, base)) {
1447                                 if(base->object->type==OB_ARMATURE)
1448                                         if(base->object->mode & OB_MODE_POSE)
1449                                                 break;
1450                         }
1451                 }
1452                 if(base)
1453                         ob= base->object;
1454         }
1455
1456
1457         if(obedit) {
1458                 ok = minmax_verts(obedit, min, max);    /* only selected */
1459         }
1460         else if(ob && (ob->mode & OB_MODE_POSE)) {
1461                 if(ob->pose) {
1462                         bArmature *arm= ob->data;
1463                         bPoseChannel *pchan;
1464                         float vec[3];
1465
1466                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
1467                                 if(pchan->bone->flag & BONE_SELECTED) {
1468                                         if(pchan->bone->layer & arm->layer) {
1469                                                 bPoseChannel *pchan_tx= pchan->custom_tx ? pchan->custom_tx : pchan;
1470                                                 ok= 1;
1471                                                 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_head);
1472                                                 DO_MINMAX(vec, min, max);
1473                                                 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_tail);
1474                                                 DO_MINMAX(vec, min, max);
1475                                         }
1476                                 }
1477                         }
1478                 }
1479         }
1480         else if (paint_facesel_test(ob)) {
1481                 ok= paintface_minmax(ob, min, max);
1482         }
1483         else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1484                 ok= PE_minmax(scene, min, max);
1485         }
1486         else {
1487                 Base *base= FIRSTBASE;
1488                 while(base) {
1489                         if(TESTBASE(v3d, base))  {
1490
1491                                 /* account for duplis */
1492                                 if (minmax_object_duplis(scene, base->object, min, max)==0)
1493                                         minmax_object(base->object, min, max); /* use if duplis not found */
1494
1495                                 ok= 1;
1496                         }
1497                         base= base->next;
1498                 }
1499         }
1500
1501         if(ok==0) return OPERATOR_FINISHED;
1502
1503         sub_v3_v3v3(afm, max, min);
1504         size= MAX3(afm[0], afm[1], afm[2]);
1505
1506         if(rv3d->persp==RV3D_ORTHO) {
1507                 if(size < 0.0001f) { /* if its a sinble point. dont even re-scale */
1508                         ok_dist= 0;
1509                 }
1510                 else {
1511                         /* perspective should be a bit farther away to look nice */
1512                         size*= 0.7f;
1513                 }
1514         }
1515         else {
1516                 if(size <= v3d->near*1.5f) {
1517                         size= v3d->near*1.5f;
1518                 }
1519         }
1520
1521         add_v3_v3v3(new_ofs, min, max);
1522         mul_v3_fl(new_ofs, -0.5f);
1523
1524         new_dist = size;
1525
1526         /* correction for window aspect ratio */
1527         if(ar->winy>2 && ar->winx>2) {
1528                 size= (float)ar->winx/(float)ar->winy;
1529                 if(size<1.0f) size= 1.0f/size;
1530                 new_dist*= size;
1531         }
1532
1533         if (rv3d->persp==RV3D_CAMOB) {
1534                 rv3d->persp= RV3D_PERSP;
1535                 smooth_view(C, v3d->camera, NULL, new_ofs, NULL, &new_dist, NULL);
1536         }
1537         else {
1538                 smooth_view(C, NULL, NULL, new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL);
1539         }
1540
1541         /* smooth view does viewlock RV3D_BOXVIEW copy */
1542         
1543 // XXX  BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1544
1545         return OPERATOR_FINISHED;
1546 }
1547
1548 void VIEW3D_OT_view_selected(wmOperatorType *ot)
1549 {
1550
1551         /* identifiers */
1552         ot->name= "View Selected";
1553         ot->description = "Move the view to the selection center";
1554         ot->idname= "VIEW3D_OT_view_selected";
1555
1556         /* api callbacks */
1557         ot->exec= viewselected_exec;
1558         ot->poll= ED_operator_region_view3d_active;
1559
1560         /* flags */
1561         ot->flag= 0;
1562 }
1563
1564 static int viewcenter_cursor_exec(bContext *C, wmOperator *UNUSED(op))
1565 {
1566         View3D *v3d = CTX_wm_view3d(C);
1567         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1568         Scene *scene= CTX_data_scene(C);
1569         
1570         if (rv3d) {
1571                 /* non camera center */
1572                 float new_ofs[3];
1573                 negate_v3_v3(new_ofs, give_cursor(scene, v3d));
1574                 smooth_view(C, NULL, NULL, new_ofs, NULL, NULL, NULL);
1575                 
1576                 /* smooth view does viewlock RV3D_BOXVIEW copy */
1577         }
1578         
1579         return OPERATOR_FINISHED;
1580 }
1581
1582 void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
1583 {
1584         /* identifiers */
1585         ot->name= "Center View to Cursor";
1586         ot->description= "Centers the view so that the cursor is in the middle of the view";
1587         ot->idname= "VIEW3D_OT_view_center_cursor";
1588         
1589         /* api callbacks */
1590         ot->exec= viewcenter_cursor_exec;
1591         ot->poll= ED_operator_view3d_active;
1592         
1593         /* flags */
1594         ot->flag= 0;
1595 }
1596
1597 static int view3d_center_camera_exec(bContext *C, wmOperator *UNUSED(op)) /* was view3d_home() in 2.4x */
1598 {
1599         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1600
1601         rv3d->camdx= rv3d->camdy= 0.0f;
1602
1603         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, CTX_wm_view3d(C));
1604
1605         return OPERATOR_FINISHED;
1606 }
1607
1608 void VIEW3D_OT_view_center_camera(wmOperatorType *ot)
1609 {
1610         /* identifiers */
1611         ot->name= "View Camera Center";
1612         ot->description = "Center the camera view";
1613         ot->idname= "VIEW3D_OT_view_center_camera";
1614
1615         /* api callbacks */
1616         ot->exec= view3d_center_camera_exec;
1617         ot->poll= view3d_camera_active_poll;
1618
1619         /* flags */
1620         ot->flag= 0;
1621 }
1622
1623 /* ********************* Set render border operator ****************** */
1624
1625 static int render_border_exec(bContext *C, wmOperator *op)
1626 {
1627         View3D *v3d = CTX_wm_view3d(C);
1628         ARegion *ar= CTX_wm_region(C);
1629         RegionView3D *rv3d= ED_view3d_context_rv3d(C);
1630         Scene *scene= CTX_data_scene(C);
1631
1632         rcti rect;
1633         rctf vb;
1634
1635         /* get border select values using rna */
1636         rect.xmin= RNA_int_get(op->ptr, "xmin");
1637         rect.ymin= RNA_int_get(op->ptr, "ymin");
1638         rect.xmax= RNA_int_get(op->ptr, "xmax");
1639         rect.ymax= RNA_int_get(op->ptr, "ymax");
1640
1641         /* calculate range */
1642         view3d_calc_camera_border(scene, ar, rv3d, v3d, &vb, FALSE);
1643
1644         scene->r.border.xmin= ((float)rect.xmin-vb.xmin)/(vb.xmax-vb.xmin);
1645         scene->r.border.ymin= ((float)rect.ymin-vb.ymin)/(vb.ymax-vb.ymin);
1646         scene->r.border.xmax= ((float)rect.xmax-vb.xmin)/(vb.xmax-vb.xmin);
1647         scene->r.border.ymax= ((float)rect.ymax-vb.ymin)/(vb.ymax-vb.ymin);
1648
1649         /* actually set border */
1650         CLAMP(scene->r.border.xmin, 0.0, 1.0);
1651         CLAMP(scene->r.border.ymin, 0.0, 1.0);
1652         CLAMP(scene->r.border.xmax, 0.0, 1.0);
1653         CLAMP(scene->r.border.ymax, 0.0, 1.0);
1654
1655         /* drawing a border surrounding the entire camera view switches off border rendering
1656          * or the border covers no pixels */
1657         if ((scene->r.border.xmin <= 0.0 && scene->r.border.xmax >= 1.0 &&
1658                 scene->r.border.ymin <= 0.0 && scene->r.border.ymax >= 1.0) ||
1659            (scene->r.border.xmin == scene->r.border.xmax ||
1660                 scene->r.border.ymin == scene->r.border.ymax ))
1661         {
1662                 scene->r.mode &= ~R_BORDER;
1663         } else {
1664                 scene->r.mode |= R_BORDER;
1665         }
1666         
1667         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS, NULL);
1668
1669         return OPERATOR_FINISHED;
1670
1671 }
1672
1673 void VIEW3D_OT_render_border(wmOperatorType *ot)
1674 {
1675         /* identifiers */
1676         ot->name= "Set Render Border";
1677         ot->description = "Set the boundries of the border render and enables border render ";
1678         ot->idname= "VIEW3D_OT_render_border";
1679
1680         /* api callbacks */
1681         ot->invoke= WM_border_select_invoke;
1682         ot->exec= render_border_exec;
1683         ot->modal= WM_border_select_modal;
1684
1685         ot->poll= view3d_camera_active_poll;
1686
1687         /* flags */
1688         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1689
1690         /* rna */
1691         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1692         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1693         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1694         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1695
1696 }
1697 /* ********************* Border Zoom operator ****************** */
1698
1699 static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
1700 {
1701         ARegion *ar= CTX_wm_region(C);
1702         View3D *v3d = CTX_wm_view3d(C);
1703         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1704         Scene *scene= CTX_data_scene(C);
1705
1706         /* Zooms in on a border drawn by the user */
1707         rcti rect;
1708         float dvec[3], vb[2], xscale, yscale, scale;
1709
1710         /* SMOOTHVIEW */
1711         float new_dist;
1712         float new_ofs[3];
1713
1714         /* ZBuffer depth vars */
1715         bglMats mats;
1716         float depth_close= FLT_MAX;
1717         double cent[2],  p[3];
1718
1719         /* note; otherwise opengl won't work */
1720         view3d_operator_needs_opengl(C);
1721
1722         /* get border select values using rna */
1723         rect.xmin= RNA_int_get(op->ptr, "xmin");
1724         rect.ymin= RNA_int_get(op->ptr, "ymin");
1725         rect.xmax= RNA_int_get(op->ptr, "xmax");
1726         rect.ymax= RNA_int_get(op->ptr, "ymax");
1727
1728         /* Get Z Depths, needed for perspective, nice for ortho */
1729         bgl_get_mats(&mats);
1730         draw_depth(scene, ar, v3d, NULL);
1731         
1732         {
1733                 /* avoid allocating the whole depth buffer */
1734                 ViewDepths depth_temp= {0};
1735
1736                 /* avoid view3d_update_depths() for speed. */
1737                 view3d_update_depths_rect(ar, &depth_temp, &rect);
1738         
1739                 /* find the closest Z pixel */
1740                 depth_close= view3d_depth_near(&depth_temp);
1741         
1742                 MEM_freeN(depth_temp.depths);
1743         }
1744
1745         cent[0] = (((double)rect.xmin)+((double)rect.xmax)) / 2;
1746         cent[1] = (((double)rect.ymin)+((double)rect.ymax)) / 2;
1747
1748         if (rv3d->persp==RV3D_PERSP) {
1749                 double p_corner[3];
1750
1751                 /* no depths to use, we cant do anything! */
1752                 if (depth_close==FLT_MAX){
1753                         BKE_report(op->reports, RPT_ERROR, "Depth Too Large");
1754                         return OPERATOR_CANCELLED;
1755                 }
1756                 /* convert border to 3d coordinates */
1757                 if ((   !gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) ||
1758                         (       !gluUnProject((double)rect.xmin, (double)rect.ymin, depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p_corner[0], &p_corner[1], &p_corner[2])))
1759                         return OPERATOR_CANCELLED;
1760
1761                 dvec[0] = p[0]-p_corner[0];
1762                 dvec[1] = p[1]-p_corner[1];
1763                 dvec[2] = p[2]-p_corner[2];
1764
1765                 new_dist = len_v3(dvec);
1766                 if(new_dist <= v3d->near*1.5) new_dist= v3d->near*1.5;
1767
1768                 new_ofs[0] = -p[0];
1769                 new_ofs[1] = -p[1];
1770                 new_ofs[2] = -p[2];
1771
1772         } else { /* othographic */
1773                 /* find the current window width and height */
1774                 vb[0] = ar->winx;
1775                 vb[1] = ar->winy;
1776
1777                 new_dist = rv3d->dist;
1778
1779                 /* convert the drawn rectangle into 3d space */
1780                 if (depth_close!=FLT_MAX && gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) {
1781                         new_ofs[0] = -p[0];
1782                         new_ofs[1] = -p[1];
1783                         new_ofs[2] = -p[2];
1784                 } else {
1785                         /* We cant use the depth, fallback to the old way that dosnt set the center depth */
1786                         copy_v3_v3(new_ofs, rv3d->ofs);
1787
1788                         initgrabz(rv3d, -new_ofs[0], -new_ofs[1], -new_ofs[2]);
1789
1790                         window_to_3d_delta(ar, dvec, (rect.xmin+rect.xmax-vb[0])/2, (rect.ymin+rect.ymax-vb[1])/2);
1791                         /* center the view to the center of the rectangle */
1792                         sub_v3_v3(new_ofs, dvec);
1793                 }
1794
1795                 /* work out the ratios, so that everything selected fits when we zoom */
1796                 xscale = ((rect.xmax-rect.xmin)/vb[0]);
1797                 yscale = ((rect.ymax-rect.ymin)/vb[1]);
1798                 scale = (xscale >= yscale)?xscale:yscale;
1799
1800                 /* zoom in as required, or as far as we can go */
1801                 new_dist = ((new_dist*scale) >= 0.001*v3d->grid)? new_dist*scale:0.001*v3d->grid;
1802         }
1803
1804         smooth_view(C, NULL, NULL, new_ofs, NULL, &new_dist, NULL);
1805
1806         if(rv3d->viewlock & RV3D_BOXVIEW)
1807                 view3d_boxview_sync(CTX_wm_area(C), ar);
1808
1809         return OPERATOR_FINISHED;
1810 }
1811
1812 static int view3d_zoom_border_invoke(bContext *C, wmOperator *op, wmEvent *event)
1813 {
1814         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1815
1816         /* if in camera view do not exec the operator so we do not conflict with set render border*/
1817         if (rv3d->persp != RV3D_CAMOB)
1818                 return WM_border_select_invoke(C, op, event);
1819         else
1820                 return OPERATOR_PASS_THROUGH;
1821 }
1822
1823 void VIEW3D_OT_zoom_border(wmOperatorType *ot)
1824 {
1825         /* identifiers */
1826         ot->name= "Border Zoom";
1827         ot->description = "Zoom in the view to the nearest object contained in the border";
1828         ot->idname= "VIEW3D_OT_zoom_border";
1829
1830         /* api callbacks */
1831         ot->invoke= view3d_zoom_border_invoke;
1832         ot->exec= view3d_zoom_border_exec;
1833         ot->modal= WM_border_select_modal;
1834
1835         ot->poll= ED_operator_region_view3d_active;
1836
1837         /* flags */
1838         ot->flag= 0;
1839
1840         /* rna */
1841         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
1842         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
1843         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
1844         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
1845
1846 }
1847
1848 /* sets the view to 1:1 camera/render-pixel */
1849 static void view3d_set_1_to_1_viewborder(Scene *scene, ARegion *ar)
1850 {
1851         RegionView3D *rv3d= ar->regiondata;
1852         float size[2];
1853         int im_width= (scene->r.size*scene->r.xsch)/100;
1854         
1855         view3d_viewborder_size_get(scene, ar, size);
1856         
1857         rv3d->camzoom= (sqrt(4.0*im_width/size[0]) - M_SQRT2)*50.0;
1858         rv3d->camzoom= CLAMPIS(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
1859 }
1860
1861 static int view3d_zoom_1_to_1_camera_exec(bContext *C, wmOperator *UNUSED(op))
1862 {
1863         Scene *scene= CTX_data_scene(C);
1864         ARegion *ar= CTX_wm_region(C);
1865
1866         view3d_set_1_to_1_viewborder(scene, ar);
1867
1868         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, CTX_wm_view3d(C));
1869
1870         return OPERATOR_FINISHED;
1871 }
1872
1873 void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot)
1874 {
1875         /* identifiers */
1876         ot->name= "Zoom Camera 1:1";
1877         ot->description = "Match the camera to 1:1 to the render output";
1878         ot->idname= "VIEW3D_OT_zoom_camera_1_to_1";
1879
1880         /* api callbacks */
1881         ot->exec= view3d_zoom_1_to_1_camera_exec;
1882         ot->poll= view3d_camera_active_poll;
1883
1884         /* flags */
1885         ot->flag= 0;
1886 }
1887
1888 /* ********************* Changing view operator ****************** */
1889
1890 static EnumPropertyItem prop_view_items[] = {
1891         {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View From the Front"},
1892         {RV3D_VIEW_BACK, "BACK", 0, "Back", "View From the Back"},
1893         {RV3D_VIEW_LEFT, "LEFT", 0, "Left", "View From the Left"},
1894         {RV3D_VIEW_RIGHT, "RIGHT", 0, "Right", "View From the Right"},
1895         {RV3D_VIEW_TOP, "TOP", 0, "Top", "View From the Top"},
1896         {RV3D_VIEW_BOTTOM, "BOTTOM", 0, "Bottom", "View From the Bottom"},
1897         {RV3D_VIEW_CAMERA, "CAMERA", 0, "Camera", "View From the active amera"},
1898         {0, NULL, 0, NULL, NULL}};
1899
1900
1901 /* would like to make this a generic function - outside of transform */
1902
1903 static void axis_set_view(bContext *C, float q1, float q2, float q3, float q4, short view, int perspo, int align_active)
1904 {
1905         View3D *v3d = CTX_wm_view3d(C);
1906         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1907         float new_quat[4];
1908
1909         new_quat[0]= q1; new_quat[1]= q2;
1910         new_quat[2]= q3; new_quat[3]= q4;
1911
1912         if(align_active) {
1913                 /* align to active object */
1914                 Object *obact= CTX_data_active_object(C);
1915                 if (obact==NULL) {
1916                         /* no active object, ignore this option */
1917                         align_active= FALSE;
1918                 }
1919                 else {
1920                         float obact_quat[4];
1921                         float twmat[3][3];
1922
1923                         /* same as transform manipulator when normal is set */
1924                         ED_getTransformOrientationMatrix(C, twmat, FALSE);
1925
1926                         mat3_to_quat( obact_quat,twmat);
1927                         invert_qt(obact_quat);
1928                         mul_qt_qtqt(new_quat, new_quat, obact_quat);
1929
1930                         rv3d->view= view= 0;
1931                 }
1932         }
1933
1934         if(align_active==FALSE) {
1935                 /* normal operation */
1936         if(rv3d->viewlock) {
1937                 /* only pass on if */
1938                         if(rv3d->view==RV3D_VIEW_FRONT && view==RV3D_VIEW_BACK);
1939                         else if(rv3d->view==RV3D_VIEW_BACK && view==RV3D_VIEW_FRONT);
1940                         else if(rv3d->view==RV3D_VIEW_RIGHT && view==RV3D_VIEW_LEFT);
1941                         else if(rv3d->view==RV3D_VIEW_LEFT && view==RV3D_VIEW_RIGHT);
1942                         else if(rv3d->view==RV3D_VIEW_BOTTOM && view==RV3D_VIEW_TOP);
1943                         else if(rv3d->view==RV3D_VIEW_TOP && view==RV3D_VIEW_BOTTOM);
1944                 else return;
1945         }
1946
1947         rv3d->view= view;
1948         }
1949
1950         if(rv3d->viewlock) {
1951                 ED_region_tag_redraw(CTX_wm_region(C));
1952                 return;
1953         }
1954
1955         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1956
1957                 if (U.uiflag & USER_AUTOPERSP) rv3d->persp= view ? RV3D_ORTHO : RV3D_PERSP;
1958                 else if(rv3d->persp==RV3D_CAMOB) rv3d->persp= perspo;
1959
1960                 smooth_view(C, v3d->camera, NULL, rv3d->ofs, new_quat, NULL, NULL);
1961         }
1962         else {
1963
1964                 if (U.uiflag & USER_AUTOPERSP) rv3d->persp= view ? RV3D_ORTHO : RV3D_PERSP;
1965                 else if(rv3d->persp==RV3D_CAMOB) rv3d->persp= perspo;
1966
1967                 smooth_view(C, NULL, NULL, NULL, new_quat, NULL, NULL);
1968         }
1969
1970 }
1971
1972 static int viewnumpad_exec(bContext *C, wmOperator *op)
1973 {
1974         View3D *v3d = CTX_wm_view3d(C);
1975         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1976         Scene *scene= CTX_data_scene(C);
1977         static int perspo=RV3D_PERSP;
1978         int viewnum, align_active, nextperspo;
1979
1980         viewnum = RNA_enum_get(op->ptr, "type");
1981         align_active = RNA_boolean_get(op->ptr, "align_active");
1982
1983         /* set this to zero, gets handled in axis_set_view */
1984         if(rv3d->viewlock)
1985                 align_active= 0;
1986
1987         /* Use this to test if we started out with a camera */
1988
1989         if (rv3d->persp == RV3D_CAMOB) {
1990                 nextperspo= rv3d->lpersp;
1991         } else {
1992                 nextperspo= perspo;
1993         }
1994
1995         switch (viewnum) {
1996                 case RV3D_VIEW_BOTTOM :
1997                         axis_set_view(C, 0.0, -1.0, 0.0, 0.0, viewnum, nextperspo, align_active);
1998                         break;
1999
2000                 case RV3D_VIEW_BACK:
2001                         axis_set_view(C, 0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0), viewnum, nextperspo, align_active);
2002                         break;
2003
2004                 case RV3D_VIEW_LEFT:
2005                         axis_set_view(C, 0.5, -0.5, 0.5, 0.5, viewnum, nextperspo, align_active);
2006                         break;
2007
2008                 case RV3D_VIEW_TOP:
2009                         axis_set_view(C, 1.0, 0.0, 0.0, 0.0, viewnum, nextperspo, align_active);
2010                         break;
2011
2012                 case RV3D_VIEW_FRONT:
2013                         axis_set_view(C, (float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0, viewnum, nextperspo, align_active);
2014                         break;
2015
2016                 case RV3D_VIEW_RIGHT:
2017                         axis_set_view(C, 0.5, -0.5, -0.5, -0.5, viewnum, nextperspo, align_active);
2018                         break;
2019
2020                 case RV3D_VIEW_CAMERA:
2021                         if(rv3d->viewlock==0) {
2022                                 /* lastview -  */
2023
2024                                 if(rv3d->persp != RV3D_CAMOB) {
2025                                         Object *ob= OBACT;
2026
2027                                         if (!rv3d->smooth_timer) {
2028                                                 /* store settings of current view before allowing overwriting with camera view
2029                                                  * only if we're not currently in a view transition */
2030                                                 copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
2031                                                 rv3d->lview= rv3d->view;
2032                                                 rv3d->lpersp= rv3d->persp;
2033                                         }
2034
2035         #if 0
2036                                         if(G.qual==LR_ALTKEY) {
2037                                                 if(oldcamera && is_an_active_object(oldcamera)) {
2038                                                         v3d->camera= oldcamera;
2039                                                 }
2040                                                 handle_view3d_lock();
2041                                         }
2042         #endif
2043                                         
2044                                         /* first get the default camera for the view lock type */
2045                                         if(v3d->scenelock) {
2046                                                 /* sets the camera view if available */
2047                                                 v3d->camera= scene->camera;                                             
2048                                         }
2049                                         else {
2050                                                 /* use scene camera if one is not set (even though we're unlocked) */
2051                                                 if(v3d->camera==NULL) {
2052                                                         v3d->camera= scene->camera;
2053                                                 }
2054                                         }
2055
2056                                         /* if the camera isnt found, check a number of options */
2057                                         if(v3d->camera==NULL && ob && ob->type==OB_CAMERA)
2058                                                 v3d->camera= ob;
2059                                         
2060                                         if(v3d->camera==NULL)
2061                                                 v3d->camera= scene_find_camera(scene);          
2062
2063                                         /* couldnt find any useful camera, bail out */
2064                                         if(v3d->camera==NULL)
2065                                                 return OPERATOR_CANCELLED;
2066                                         
2067                                         /* important these dont get out of sync for locked scenes */
2068                                         if(v3d->scenelock)
2069                                                 scene->camera= v3d->camera;
2070
2071                                         /* finally do snazzy view zooming */
2072                                         rv3d->persp= RV3D_CAMOB;
2073                                         smooth_view(C, NULL, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
2074
2075                                 }
2076                                 else{
2077                                         /* return to settings of last view */
2078                                         /* does smooth_view too */
2079                                         axis_set_view(C, rv3d->lviewquat[0], rv3d->lviewquat[1], rv3d->lviewquat[2], rv3d->lviewquat[3], rv3d->lview, rv3d->lpersp, 0);
2080                                 }
2081                         }
2082                         break;
2083
2084                 default :
2085                         break;
2086         }
2087
2088         if(rv3d->persp != RV3D_CAMOB) perspo= rv3d->persp;
2089
2090         return OPERATOR_FINISHED;
2091 }
2092
2093
2094 void VIEW3D_OT_viewnumpad(wmOperatorType *ot)
2095 {
2096         /* identifiers */
2097         ot->name= "View numpad";
2098         ot->description = "Set the view";
2099         ot->idname= "VIEW3D_OT_viewnumpad";
2100
2101         /* api callbacks */
2102         ot->exec= viewnumpad_exec;
2103         ot->poll= ED_operator_region_view3d_active;
2104
2105         /* flags */
2106         ot->flag= 0;
2107
2108         RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "The Type of view");
2109         RNA_def_boolean(ot->srna, "align_active", 0, "Align Active", "Align to the active objects axis");
2110 }
2111
2112 static EnumPropertyItem prop_view_orbit_items[] = {
2113         {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the Left"},
2114         {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the Right"},
2115         {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view Up"},
2116         {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view Down"},
2117         {0, NULL, 0, NULL, NULL}};
2118
2119 static int vieworbit_exec(bContext *C, wmOperator *op)
2120 {
2121         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2122         float phi, q1[4], new_quat[4];
2123         int orbitdir;
2124
2125         orbitdir = RNA_enum_get(op->ptr, "type");
2126
2127         if(rv3d->viewlock==0) {
2128
2129                 if(rv3d->persp != RV3D_CAMOB) {
2130                         if(orbitdir == V3D_VIEW_STEPLEFT || orbitdir == V3D_VIEW_STEPRIGHT) {
2131                                 float si;
2132                                 /* z-axis */
2133                                 phi= (float)(M_PI/360.0)*U.pad_rot_angle;
2134                                 if(orbitdir == V3D_VIEW_STEPRIGHT) phi= -phi;
2135                                 si= (float)sin(phi);
2136                                 q1[0]= (float)cos(phi);
2137                                 q1[1]= q1[2]= 0.0;
2138                                 q1[3]= si;
2139                                 mul_qt_qtqt(new_quat, rv3d->viewquat, q1);
2140                                 rv3d->view= 0;
2141                         }
2142                         else if(orbitdir == V3D_VIEW_STEPDOWN || orbitdir == V3D_VIEW_STEPUP) {
2143                                 /* horizontal axis */
2144                                 copy_v3_v3(q1+1, rv3d->viewinv[0]);
2145
2146                                 normalize_v3(q1+1);
2147                                 phi= (float)(M_PI/360.0)*U.pad_rot_angle;
2148                                 if(orbitdir == V3D_VIEW_STEPDOWN) phi= -phi;
2149                                 q1[0]= (float)cos(phi);
2150                                 mul_v3_fl(q1+1, sin(phi));
2151                                 mul_qt_qtqt(new_quat, rv3d->viewquat, q1);
2152                                 rv3d->view= 0;
2153                         }
2154
2155                         smooth_view(C, NULL, NULL, NULL, new_quat, NULL, NULL);
2156                 }
2157         }
2158
2159         return OPERATOR_FINISHED;
2160 }
2161
2162 void VIEW3D_OT_view_orbit(wmOperatorType *ot)
2163 {
2164         /* identifiers */
2165         ot->name= "View Orbit";
2166         ot->description = "Orbit the view";
2167         ot->idname= "VIEW3D_OT_view_orbit";
2168
2169         /* api callbacks */
2170         ot->exec= vieworbit_exec;
2171         ot->poll= ED_operator_region_view3d_active;
2172
2173         /* flags */
2174         ot->flag= 0;
2175         RNA_def_enum(ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit");
2176 }
2177
2178 static EnumPropertyItem prop_view_pan_items[] = {
2179         {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the Left"},
2180         {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the Right"},
2181         {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view Up"},
2182         {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view Down"},
2183         {0, NULL, 0, NULL, NULL}};
2184
2185 static int viewpan_exec(bContext *C, wmOperator *op)
2186 {
2187         ARegion *ar= CTX_wm_region(C);
2188         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2189         float vec[3];
2190         int pandir;
2191
2192         pandir = RNA_enum_get(op->ptr, "type");
2193
2194         initgrabz(rv3d, 0.0, 0.0, 0.0);
2195         if(pandir == V3D_VIEW_PANRIGHT) window_to_3d_delta(ar, vec, -32, 0);
2196         else if(pandir == V3D_VIEW_PANLEFT) window_to_3d_delta(ar, vec, 32, 0);
2197         else if(pandir == V3D_VIEW_PANUP) window_to_3d_delta(ar, vec, 0, -25);
2198         else if(pandir == V3D_VIEW_PANDOWN) window_to_3d_delta(ar, vec, 0, 25);
2199         add_v3_v3(rv3d->ofs, vec);
2200
2201         if(rv3d->viewlock & RV3D_BOXVIEW)
2202                 view3d_boxview_sync(CTX_wm_area(C), ar);
2203
2204         ED_region_tag_redraw(ar);
2205
2206         return OPERATOR_FINISHED;
2207 }
2208
2209 void VIEW3D_OT_view_pan(wmOperatorType *ot)
2210 {
2211         /* identifiers */
2212         ot->name= "View Pan";
2213         ot->description = "Pan the view";
2214         ot->idname= "VIEW3D_OT_view_pan";
2215
2216         /* api callbacks */
2217         ot->exec= viewpan_exec;
2218         ot->poll= ED_operator_region_view3d_active;
2219
2220         /* flags */
2221         ot->flag= 0;
2222         RNA_def_enum(ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan");
2223 }
2224
2225 static int viewpersportho_exec(bContext *C, wmOperator *UNUSED(op))
2226 {
2227         ARegion *ar= CTX_wm_region(C);
2228         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2229
2230         if(rv3d->viewlock==0) {
2231                 if(rv3d->persp!=RV3D_ORTHO)
2232                         rv3d->persp=RV3D_ORTHO;
2233                 else rv3d->persp=RV3D_PERSP;
2234                 ED_region_tag_redraw(ar);
2235         }
2236
2237         return OPERATOR_FINISHED;
2238
2239 }
2240
2241 void VIEW3D_OT_view_persportho(wmOperatorType *ot)
2242 {
2243         /* identifiers */
2244         ot->name= "View Persp/Ortho";
2245         ot->description = "Switch the current view from perspective/orthographic";
2246         ot->idname= "VIEW3D_OT_view_persportho";
2247
2248         /* api callbacks */
2249         ot->exec= viewpersportho_exec;
2250         ot->poll= ED_operator_region_view3d_active;
2251
2252         /* flags */
2253         ot->flag= 0;
2254 }
2255
2256 /* ******************** add background image operator **************** */
2257
2258 static BGpic *background_image_add(bContext *C)
2259 {
2260         View3D *v3d= CTX_wm_view3d(C);
2261         
2262         BGpic *bgpic= MEM_callocN(sizeof(BGpic), "Background Image");
2263         bgpic->size= 5.0;
2264         bgpic->blend= 0.5;
2265         bgpic->iuser.fie_ima= 2;
2266         bgpic->iuser.ok= 1;
2267         bgpic->view= 0; /* 0 for all */
2268         
2269         BLI_addtail(&v3d->bgpicbase, bgpic);
2270         
2271         return bgpic;
2272 }
2273
2274 static int background_image_add_exec(bContext *C, wmOperator *UNUSED(op))
2275 {
2276         background_image_add(C);
2277
2278         return OPERATOR_FINISHED;
2279 }
2280
2281 static int background_image_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2282 {
2283         View3D *v3d= CTX_wm_view3d(C);
2284         Image *ima= NULL;
2285         BGpic *bgpic;
2286         char name[32];
2287         
2288         /* check input variables */
2289         if(RNA_property_is_set(op->ptr, "filepath")) {
2290                 char path[FILE_MAX];
2291                 
2292                 RNA_string_get(op->ptr, "filepath", path);
2293                 ima= BKE_add_image_file(path);
2294         }
2295         else if(RNA_property_is_set(op->ptr, "name")) {
2296                 RNA_string_get(op->ptr, "name", name);
2297                 ima= (Image *)find_id("IM", name);
2298         }
2299         
2300         bgpic = background_image_add(C);
2301         
2302         if (ima) {
2303                 bgpic->ima = ima;
2304                 
2305                 if(ima->id.us==0) id_us_plus(&ima->id);
2306                 else id_lib_extern(&ima->id);
2307                 
2308                 if (!(v3d->flag & V3D_DISPBGPICS))
2309                         v3d->flag |= V3D_DISPBGPICS;
2310         }
2311         
2312         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2313         
2314         return OPERATOR_FINISHED;
2315 }
2316
2317 void VIEW3D_OT_background_image_add(wmOperatorType *ot)
2318 {
2319         /* identifiers */
2320         ot->name   = "Add Background Image";
2321         ot->description= "Add a new background image";
2322         ot->idname = "VIEW3D_OT_background_image_add";
2323
2324         /* api callbacks */
2325         ot->invoke = background_image_add_invoke;
2326         ot->exec   = background_image_add_exec;
2327         ot->poll   = ED_operator_view3d_active;
2328
2329         /* flags */
2330         ot->flag   = 0;
2331         
2332         /* properties */
2333         RNA_def_string(ot->srna, "name", "Image", 24, "Name", "Image name to assign.");
2334         RNA_def_string(ot->srna, "filepath", "Path", FILE_MAX, "Filepath", "Path to image file");
2335 }
2336
2337
2338 /* ***** remove image operator ******* */
2339 static int background_image_remove_exec(bContext *C, wmOperator *op)
2340 {
2341         View3D *vd = CTX_wm_view3d(C);
2342         int index = RNA_int_get(op->ptr, "index");
2343         BGpic *bgpic_rem= BLI_findlink(&vd->bgpicbase, index);
2344
2345         if(bgpic_rem) {
2346                 BLI_remlink(&vd->bgpicbase, bgpic_rem);
2347                 if(bgpic_rem->ima) bgpic_rem->ima->id.us--;
2348                 MEM_freeN(bgpic_rem);
2349                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, vd);
2350                 return OPERATOR_FINISHED;
2351         }
2352         else {
2353                 return OPERATOR_CANCELLED;
2354         }
2355
2356 }
2357
2358 void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
2359 {
2360         /* identifiers */
2361         ot->name   = "Remove Background Image";
2362         ot->description= "Remove a background image from the 3D view";
2363         ot->idname = "VIEW3D_OT_background_image_remove";
2364
2365         /* api callbacks */
2366         ot->exec   = background_image_remove_exec;
2367         ot->poll   = ED_operator_view3d_active;
2368
2369         /* flags */
2370         ot->flag   = 0;
2371
2372         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Background image index to remove ", 0, INT_MAX);
2373 }
2374
2375 /* ********************* set clipping operator ****************** */
2376
2377 static void calc_clipping_plane(float clip[6][4], BoundBox *clipbb)
2378 {
2379         int val;
2380
2381         for(val=0; val<4; val++) {
2382
2383                 normal_tri_v3( clip[val],clipbb->vec[val], clipbb->vec[val==3?0:val+1], clipbb->vec[val+4]);
2384
2385                 clip[val][3]=
2386                         - clip[val][0]*clipbb->vec[val][0]
2387                         - clip[val][1]*clipbb->vec[val][1]
2388                         - clip[val][2]*clipbb->vec[val][2];
2389         }
2390 }
2391
2392 static void calc_local_clipping(float clip_local[][4], BoundBox *clipbb, float mat[][4])
2393 {
2394         BoundBox clipbb_local;
2395         float imat[4][4];
2396         int i;
2397
2398         invert_m4_m4(imat, mat);
2399
2400         for(i=0; i<8; i++) {
2401                 mul_v3_m4v3(clipbb_local.vec[i], imat, clipbb->vec[i]);
2402         }
2403
2404         calc_clipping_plane(clip_local, &clipbb_local);
2405 }
2406
2407 void ED_view3d_local_clipping(RegionView3D *rv3d, float mat[][4])
2408 {
2409         if(rv3d->rflag & RV3D_CLIPPING)
2410                 calc_local_clipping(rv3d->clip_local, rv3d->clipbb, mat);
2411 }
2412
2413 static int view3d_clipping_exec(bContext *C, wmOperator *op)
2414 {
2415         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2416         ViewContext vc;
2417         bglMats mats;
2418         rcti rect;
2419
2420         rect.xmin= RNA_int_get(op->ptr, "xmin");
2421         rect.ymin= RNA_int_get(op->ptr, "ymin");
2422         rect.xmax= RNA_int_get(op->ptr, "xmax");
2423         rect.ymax= RNA_int_get(op->ptr, "ymax");
2424
2425         rv3d->rflag |= RV3D_CLIPPING;
2426         rv3d->clipbb= MEM_callocN(sizeof(BoundBox), "clipbb");
2427
2428         /* note; otherwise opengl won't work */
2429         view3d_operator_needs_opengl(C);
2430
2431         view3d_set_viewcontext(C, &vc);
2432         view3d_get_transformation(vc.ar, vc.rv3d, NULL, &mats); /* NULL because we don't want it in object space */
2433         view3d_calculate_clipping(rv3d->clipbb, rv3d->clip, &mats, &rect);
2434
2435         return OPERATOR_FINISHED;
2436 }
2437
2438 static int view3d_clipping_invoke(bContext *C, wmOperator *op, wmEvent *event)
2439 {
2440         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2441         ARegion *ar= CTX_wm_region(C);
2442
2443         if(rv3d->rflag & RV3D_CLIPPING) {
2444                 rv3d->rflag &= ~RV3D_CLIPPING;
2445                 ED_region_tag_redraw(ar);
2446                 if(rv3d->clipbb) MEM_freeN(rv3d->clipbb);
2447                 rv3d->clipbb= NULL;
2448                 return OPERATOR_FINISHED;
2449         }
2450         else {
2451                 return WM_border_select_invoke(C, op, event);
2452         }
2453 }
2454
2455 /* toggles */
2456 void VIEW3D_OT_clip_border(wmOperatorType *ot)
2457 {
2458
2459         /* identifiers */
2460         ot->name= "Clipping Border";
2461         ot->description = "Set the view clipping border";
2462         ot->idname= "VIEW3D_OT_clip_border";
2463
2464         /* api callbacks */
2465         ot->invoke= view3d_clipping_invoke;
2466         ot->exec= view3d_clipping_exec;
2467         ot->modal= WM_border_select_modal;
2468
2469         ot->poll= ED_operator_region_view3d_active;
2470
2471         /* flags */
2472         ot->flag= 0;
2473
2474         /* rna */
2475         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2476         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2477         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2478         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2479 }
2480
2481 /* ***************** 3d cursor cursor op ******************* */
2482
2483 /* mx my in region coords */
2484 static int set_3dcursor_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2485 {
2486         Scene *scene= CTX_data_scene(C);
2487         ARegion *ar= CTX_wm_region(C);
2488         View3D *v3d = CTX_wm_view3d(C);
2489         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2490         float dx, dy, fz, *fp = NULL, dvec[3], oldcurs[3];
2491         short mx, my, mval[2];
2492 //      short ctrl= 0; // XXX
2493         int flip;
2494         fp= give_cursor(scene, v3d);
2495
2496 //      if(obedit && ctrl) lr_click= 1;
2497         copy_v3_v3(oldcurs, fp);
2498
2499         mx= event->x - ar->winrct.xmin;
2500         my= event->y - ar->winrct.ymin;
2501
2502         project_short_noclip(ar, fp, mval);
2503         flip= initgrabz(rv3d, fp[0], fp[1], fp[2]);
2504         
2505         /* reset the depth based on the view offset */
2506         if(flip) {
2507                 negate_v3_v3(fp, rv3d->ofs);
2508
2509                 /* re initialize */
2510                 project_short_noclip(ar, fp, mval);
2511                 flip= initgrabz(rv3d, fp[0], fp[1], fp[2]);
2512         }
2513
2514         if(mval[0]!=IS_CLIPPED) {
2515                 short depth_used = 0;
2516
2517                 if (U.uiflag & USER_ORBIT_ZBUF) { /* maybe this should be accessed some other way */
2518                         short mval_depth[2];
2519                         mval_depth[0]= mx;
2520                         mval_depth[1]= my;
2521                         view3d_operator_needs_opengl(C);
2522                         if (view_autodist(scene, ar, v3d, mval_depth, fp))
2523                                 depth_used= 1;
2524                 }
2525
2526                 if(depth_used==0) {
2527                         window_to_3d_delta(ar, dvec, mval[0]-mx, mval[1]-my);
2528                         sub_v3_v3(fp, dvec);
2529                 }
2530         }
2531         else {
2532
2533                 dx= ((float)(mx-(ar->winx/2)))*rv3d->zfac/(ar->winx/2);
2534                 dy= ((float)(my-(ar->winy/2)))*rv3d->zfac/(ar->winy/2);
2535
2536                 fz= rv3d->persmat[0][3]*fp[0]+ rv3d->persmat[1][3]*fp[1]+ rv3d->persmat[2][3]*fp[2]+ rv3d->persmat[3][3];
2537                 fz= fz/rv3d->zfac;
2538
2539                 fp[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy+ rv3d->persinv[2][0]*fz)-rv3d->ofs[0];
2540                 fp[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy+ rv3d->persinv[2][1]*fz)-rv3d->ofs[1];
2541                 fp[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy+ rv3d->persinv[2][2]*fz)-rv3d->ofs[2];
2542         }
2543
2544         if(v3d && v3d->localvd)
2545                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2546         else
2547                 WM_event_add_notifier(C, NC_SCENE|NA_EDITED, scene);
2548
2549         return OPERATOR_FINISHED;
2550 }
2551
2552 void VIEW3D_OT_cursor3d(wmOperatorType *ot)
2553 {
2554
2555         /* identifiers */
2556         ot->name= "Set 3D Cursor";
2557         ot->description = "Set the location of the 3D cursor";
2558         ot->idname= "VIEW3D_OT_cursor3d";
2559
2560         /* api callbacks */
2561         ot->invoke= set_3dcursor_invoke;
2562
2563         ot->poll= ED_operator_view3d_active;
2564     
2565         /* flags */
2566 //      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2567     
2568         /* rna later */
2569
2570 }
2571
2572 /* ***************** manipulator op ******************* */
2573
2574
2575 static int manipulator_invoke(bContext *C, wmOperator *op, wmEvent *event)
2576 {
2577         View3D *v3d = CTX_wm_view3d(C);
2578
2579         if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
2580         if(!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
2581
2582         /* only no modifier or shift */
2583         if(event->keymodifier != 0 && event->keymodifier != KM_SHIFT) return OPERATOR_PASS_THROUGH;
2584
2585         /* note; otherwise opengl won't work */
2586         view3d_operator_needs_opengl(C);
2587
2588         if(0==BIF_do_manipulator(C, event, op))
2589                 return OPERATOR_PASS_THROUGH;
2590
2591         return OPERATOR_FINISHED;
2592 }
2593
2594 void VIEW3D_OT_manipulator(wmOperatorType *ot)
2595 {
2596
2597         /* identifiers */
2598         ot->name= "3D Manipulator";
2599         ot->description = "Manipulate selected item by axis";
2600         ot->idname= "VIEW3D_OT_manipulator";
2601
2602         /* api callbacks */
2603         ot->invoke= manipulator_invoke;
2604
2605         ot->poll= ED_operator_view3d_active;
2606
2607         /* properties to pass to transform */
2608         Transform_Properties(ot, P_CONSTRAINT);
2609 }
2610
2611 static int enable_manipulator_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2612 {
2613         View3D *v3d = CTX_wm_view3d(C);
2614
2615         v3d->twtype=0;
2616         
2617         if (RNA_boolean_get(op->ptr, "translate"))
2618                 v3d->twtype |= V3D_MANIP_TRANSLATE;
2619         if (RNA_boolean_get(op->ptr, "rotate"))
2620                 v3d->twtype |= V3D_MANIP_ROTATE;
2621         if (RNA_boolean_get(op->ptr, "scale"))
2622                 v3d->twtype |= V3D_MANIP_SCALE;
2623                 
2624         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2625
2626         return OPERATOR_FINISHED;
2627 }
2628
2629 void VIEW3D_OT_enable_manipulator(wmOperatorType *ot)
2630 {
2631         /* identifiers */
2632         ot->name= "Enable 3D Manipulator";
2633         ot->description = "Enable the transform manipulator for use";
2634         ot->idname= "VIEW3D_OT_enable_manipulator";
2635         
2636         /* api callbacks */
2637         ot->invoke= enable_manipulator_invoke;
2638         ot->poll= ED_operator_view3d_active;
2639         
2640         /* rna later */
2641         RNA_def_boolean(ot->srna, "translate", 0, "Translate", "Enable the translate manipulator");
2642         RNA_def_boolean(ot->srna, "rotate", 0, "Rotate", "Enable the rotate manipulator");
2643         RNA_def_boolean(ot->srna, "scale", 0, "Scale", "Enable the scale manipulator");
2644 }
2645
2646 /* ************************* below the line! *********************** */
2647
2648
2649 static float view_autodist_depth_margin(ARegion *ar, short mval[2], int margin)
2650 {
2651         ViewDepths depth_temp= {0};
2652         rcti rect;
2653         float depth_close;
2654
2655         if(margin==0) {
2656                 /* Get Z Depths, needed for perspective, nice for ortho */
2657                 rect.xmin= mval[0];
2658                 rect.ymin= mval[1];
2659                 rect.xmax= mval[0] + 1;
2660                 rect.ymax= mval[1] + 1;
2661         }
2662         else {
2663                 rect.xmax = mval[0] + margin;
2664                 rect.ymax = mval[1] + margin;
2665
2666                 rect.xmin = mval[0] - margin;
2667                 rect.ymin = mval[1] - margin;
2668         }
2669
2670         view3d_update_depths_rect(ar, &depth_temp, &rect);
2671         depth_close= view3d_depth_near(&depth_temp);
2672         if(depth_temp.depths) MEM_freeN(depth_temp.depths);
2673         return depth_close;
2674 }
2675
2676 /* XXX todo Zooms in on a border drawn by the user */
2677 int view_autodist(Scene *scene, ARegion *ar, View3D *v3d, short *mval, float mouse_worldloc[3] ) //, float *autodist )
2678 {
2679         bglMats mats; /* ZBuffer depth vars */
2680         float depth_close= FLT_MAX;
2681         double cent[2],  p[3];
2682
2683         /* Get Z Depths, needed for perspective, nice for ortho */
2684         bgl_get_mats(&mats);
2685         draw_depth(scene, ar, v3d, NULL);
2686
2687         depth_close= view_autodist_depth_margin(ar, mval, 4);
2688
2689         if (depth_close==FLT_MAX)
2690                 return 0;
2691
2692         cent[0] = (double)mval[0];
2693         cent[1] = (double)mval[1];
2694
2695         if (!gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
2696                 return 0;
2697
2698         mouse_worldloc[0] = (float)p[0];
2699         mouse_worldloc[1] = (float)p[1];
2700         mouse_worldloc[2] = (float)p[2];
2701         return 1;
2702 }
2703
2704 int view_autodist_init(Scene *scene, ARegion *ar, View3D *v3d, int mode) //, float *autodist )
2705 {
2706         /* Get Z Depths, needed for perspective, nice for ortho */
2707         switch(mode) {
2708         case 0:
2709                 draw_depth(scene, ar, v3d, NULL);
2710                 break;
2711         case 1:
2712                 draw_depth_gpencil(scene, ar, v3d);
2713                 break;
2714         }
2715
2716         return 1;
2717 }
2718
2719 // no 4x4 sampling, run view_autodist_init first
2720 int view_autodist_simple(ARegion *ar, short *mval, float mouse_worldloc[3], int margin, float *force_depth) //, float *autodist )
2721 {
2722         bglMats mats; /* ZBuffer depth vars, could cache? */
2723         float depth;
2724         double cent[2],  p[3];
2725
2726         /* Get Z Depths, needed for perspective, nice for ortho */
2727         if(force_depth)
2728                 depth= *force_depth;
2729         else
2730                 depth= view_autodist_depth_margin(ar, mval, margin);
2731
2732         if (depth==FLT_MAX)
2733                 return 0;
2734
2735         cent[0] = (double)mval[0];
2736         cent[1] = (double)mval[1];
2737
2738         bgl_get_mats(&mats);
2739         if (!gluUnProject(cent[0], cent[1], depth, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
2740                 return 0;
2741
2742         mouse_worldloc[0] = (float)p[0];
2743         mouse_worldloc[1] = (float)p[1];
2744         mouse_worldloc[2] = (float)p[2];
2745         return 1;
2746 }
2747
2748 int view_autodist_depth(struct ARegion *ar, short mval[2], int margin, float *depth)
2749 {
2750         *depth= view_autodist_depth_margin(ar, mval, margin);
2751
2752         return (*depth==FLT_MAX) ? 0:1;
2753 }
2754
2755 static int depth_segment_cb(int x, int y, void *userData)
2756 {
2757         struct { struct ARegion *ar; int margin; float depth; } *data = userData;
2758         short mval[2];
2759         float depth;
2760
2761         mval[0]= (short)x;
2762         mval[1]= (short)y;
2763
2764         depth= view_autodist_depth_margin(data->ar, mval, data->margin);
2765
2766         if(depth != FLT_MAX) {
2767                 data->depth= depth;
2768                 return 0;
2769         }
2770         else {
2771                 return 1;
2772         }
2773 }
2774
2775 int view_autodist_depth_segment(struct ARegion *ar, short mval_sta[2], short mval_end[2], int margin, float *depth)
2776 {
2777         struct { struct ARegion *ar; int margin; float depth; } data = {0};
2778         int p1[2];
2779         int p2[2];
2780
2781         data.ar= ar;
2782         data.margin= margin;
2783         data.depth= FLT_MAX;
2784
2785         VECCOPY2D(p1, mval_sta);
2786         VECCOPY2D(p2, mval_end);
2787
2788         plot_line_v2v2i(p1, p2, depth_segment_cb, &data);
2789
2790         *depth= data.depth;
2791
2792         return (*depth==FLT_MAX) ? 0:1;
2793 }
2794
2795 /* ********************* NDOF ************************ */
2796 /* note: this code is confusing and unclear... (ton) */
2797 /* **************************************************** */
2798
2799 // ndof scaling will be moved to user setting.
2800 // In the mean time this is just a place holder.
2801
2802 // Note: scaling in the plugin and ghostwinlay.c
2803 // should be removed. With driver default setting,
2804 // each axis returns approx. +-200 max deflection.
2805
2806 // The values I selected are based on the older
2807 // polling i/f. With event i/f, the sensistivity
2808 // can be increased for improved response from
2809 // small deflections of the device input.
2810
2811
2812 // lukep notes : i disagree on the range.
2813 // the normal 3Dconnection driver give +/-400
2814 // on defaut range in other applications
2815 // and up to +/- 1000 if set to maximum
2816 // because i remove the scaling by delta,
2817 // which was a bad idea as it depend of the system
2818 // speed and os, i changed the scaling values, but
2819 // those are still not ok
2820
2821 #if 0
2822 static float ndof_axis_scale[6] = {
2823         +0.01,  // Tx
2824         +0.01,  // Tz
2825         +0.01,  // Ty
2826         +0.0015,        // Rx
2827         +0.0015,        // Rz
2828         +0.0015 // Ry
2829 };
2830
2831 static void filterNDOFvalues(float *sbval)
2832 {
2833         int i=0;
2834         float max  = 0.0;
2835
2836         for (i =0; i<6;i++)
2837                 if (fabs(sbval[i]) > max)
2838                         max = fabs(sbval[i]);
2839         for (i =0; i<6;i++)
2840                 if (fabs(sbval[i]) != max )
2841                         sbval[i]=0.0;
2842 }
2843
2844 // statics for controlling rv3d->dist corrections.
2845 // viewmoveNDOF zeros and adjusts rv3d->ofs.
2846 // viewmove restores based on dz_flag state.
2847
2848 int dz_flag = 0;
2849 float m_dist;
2850
2851 void viewmoveNDOFfly(ARegion *ar, View3D *v3d, int UNUSED(mode))
2852 {
2853         RegionView3D *rv3d= ar->regiondata;
2854         int i;
2855         float phi;
2856         float dval[7];
2857         // static fval[6] for low pass filter; device input vector is dval[6]
2858         static float fval[6];
2859         float tvec[3],rvec[3];
2860         float q1[4];
2861         float mat[3][3];
2862         float upvec[3];
2863
2864
2865         /*----------------------------------------------------
2866          * sometimes this routine is called from headerbuttons
2867          * viewmove needs to refresh the screen
2868          */
2869 // XXX  areawinset(ar->win);
2870
2871
2872         // fetch the current state of the ndof device
2873 // XXX  getndof(dval);
2874
2875         if (v3d->ndoffilter)
2876                 filterNDOFvalues(fval);
2877
2878         // Scale input values
2879
2880 //      if(dval[6] == 0) return; // guard against divide by zero
2881
2882         for(i=0;i<6;i++) {
2883
2884                 // user scaling
2885                 dval[i] = dval[i] * ndof_axis_scale[i];
2886         }
2887
2888
2889         // low pass filter with zero crossing reset
2890
2891         for(i=0;i<6;i++) {
2892                 if((dval[i] * fval[i]) >= 0)
2893                         dval[i] = (fval[i] * 15 + dval[i]) / 16;
2894                 else
2895                         fval[i] = 0;
2896         }
2897
2898
2899         // force perspective mode. This is a hack and is
2900         // incomplete. It doesn't actually effect the view
2901         // until the first draw and doesn't update the menu
2902         // to reflect persp mode.
2903
2904         rv3d->persp = RV3D_PERSP;
2905
2906
2907         // Correct the distance jump if rv3d->dist != 0
2908
2909         // This is due to a side effect of the original
2910         // mouse view rotation code. The rotation point is
2911         // set a distance in front of the viewport to
2912         // make rotating with the mouse look better.
2913         // The distance effect is written at a low level
2914         // in the view management instead of the mouse
2915         // view function. This means that all other view
2916         // movement devices must subtract this from their
2917         // view transformations.
2918
2919         if(rv3d->dist != 0.0) {
2920                 dz_flag = 1;
2921                 m_dist = rv3d->dist;
2922                 upvec[0] = upvec[1] = 0;
2923                 upvec[2] = rv3d->dist;
2924                 copy_m3_m4(mat, rv3d->viewinv);
2925                 mul_m3_v3(mat, upvec);
2926                 sub_v3_v3(rv3d->ofs, upvec);
2927                 rv3d->dist = 0.0;
2928         }
2929
2930
2931         // Apply rotation
2932         // Rotations feel relatively faster than translations only in fly mode, so
2933         // we have no choice but to fix that here (not in the plugins)
2934         rvec[0] = -0.5 * dval[3];
2935         rvec[1] = -0.5 * dval[4];
2936         rvec[2] = -0.5 * dval[5];
2937
2938         // rotate device x and y by view z
2939
2940         copy_m3_m4(mat, rv3d->viewinv);
2941         mat[2][2] = 0.0f;
2942         mul_m3_v3(mat, rvec);
2943
2944         // rotate the view
2945
2946         phi = normalize_v3(rvec);
2947         if(phi != 0) {
2948                 axis_angle_to_quat(q1,rvec,phi);
2949                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
2950         }
2951
2952
2953         // Apply translation
2954
2955         tvec[0] = dval[0];
2956         tvec[1] = dval[1];
2957         tvec[2] = -dval[2];
2958
2959         // the next three lines rotate the x and y translation coordinates
2960         // by the current z axis angle
2961
2962         copy_m3_m4(mat, rv3d->viewinv);
2963         mat[2][2] = 0.0f;
2964         mul_m3_v3(mat, tvec);
2965
2966         // translate the view
2967
2968         sub_v3_v3(rv3d->ofs, tvec);
2969
2970
2971         /*----------------------------------------------------
2972          * refresh the screen XXX
2973           */
2974
2975         // update render preview window
2976
2977 // XXX  BIF_view3d_previewrender_signal(ar, PR_DBASE|PR_DISPRECT);
2978 }
2979
2980 void viewmoveNDOF(Scene *scene, ARegion *ar, View3D *v3d, int UNUSED(mode))
2981 {
2982         RegionView3D *rv3d= ar->regiondata;
2983         float fval[7];
2984         float dvec[3];
2985         float sbadjust = 1.0f;
2986         float len;
2987         short use_sel = 0;
2988         Object *ob = OBACT;
2989         float m[3][3];
2990         float m_inv[3][3];
2991         float xvec[3] = {1,0,0};
2992         float yvec[3] = {0,-1,0};
2993         float zvec[3] = {0,0,1};
2994         float phi;
2995         float q1[4];
2996         float obofs[3];
2997         float reverse;
2998         //float diff[4];
2999         float d, curareaX, curareaY;
3000         float mat[3][3];
3001         float upvec[3];
3002
3003         /* Sensitivity will control how fast the view rotates.  The value was
3004          * obtained experimentally by tweaking until the author didn't get dizzy watching.
3005          * Perhaps this should be a configurable user parameter.
3006          */
3007         float psens = 0.005f * (float) U.ndof_pan;   /* pan sensitivity */
3008         float rsens = 0.005f * (float) U.ndof_rotate;  /* rotate sensitivity */
3009         float zsens = 0.3f;   /* zoom sensitivity */
3010
3011         const float minZoom = -30.0f;
3012         const float maxZoom = 300.0f;
3013
3014         //reset view type
3015         rv3d->view = 0;
3016 //printf("passing here \n");
3017 //
3018         if (scene->obedit==NULL && ob && !(ob->mode & OB_MODE_POSE)) {
3019                 use_sel = 1;
3020         }
3021
3022         if((dz_flag)||rv3d->dist==0) {
3023                 dz_flag = 0;
3024                 rv3d->dist = m_dist;
3025                 upvec[0] = upvec[1] = 0;
3026                 upvec[2] = rv3d->dist;
3027                 copy_m3_m4(mat, rv3d->viewinv);
3028                 mul_m3_v3(mat, upvec);
3029                 add_v3_v3(rv3d->ofs, upvec);
3030         }
3031
3032         /*----------------------------------------------------
3033          * sometimes this routine is called from headerbuttons
3034          * viewmove needs to refresh the screen
3035          */
3036 // XXX  areawinset(curarea->win);
3037
3038         /*----------------------------------------------------
3039          * record how much time has passed. clamp at 10 Hz
3040          * pretend the previous frame occurred at the clamped time
3041          */
3042 //    now = PIL_check_seconds_timer();
3043  //   frametime = (now - prevTime);
3044  //   if (frametime > 0.1f){        /* if more than 1/10s */
3045  //       frametime = 1.0f/60.0;      /* clamp at 1/60s so no jumps when starting to move */
3046 //    }
3047 //    prevTime = now;
3048  //   sbadjust *= 60 * frametime;             /* normalize ndof device adjustments to 100Hz for framerate independence */
3049
3050         /* fetch the current state of the ndof device & enforce dominant mode if selected */
3051 // XXX    getndof(fval);
3052         if (v3d->ndoffilter)
3053                 filterNDOFvalues(fval);
3054
3055
3056         // put scaling back here, was previously in ghostwinlay
3057         fval[0] = fval[0] * (1.0f/600.0f);
3058         fval[1] = fval[1] * (1.0f/600.0f);
3059         fval[2] = fval[2] * (1.0f/1100.0f);
3060         fval[3] = fval[3] * 0.00005f;
3061         fval[4] =-fval[4] * 0.00005f;
3062         fval[5] = fval[5] * 0.00005f;
3063         fval[6] = fval[6] / 1000000.0f;
3064
3065         // scale more if not in perspective mode
3066         if (rv3d->persp == RV3D_ORTHO) {
3067                 fval[0] = fval[0] * 0.05f;
3068                 fval[1] = fval[1] * 0.05f;
3069                 fval[2] = fval[2] * 0.05f;
3070                 fval[3] = fval[3] * 0.9f;
3071                 fval[4] = fval[4] * 0.9f;
3072                 fval[5] = fval[5] * 0.9f;
3073                 zsens *= 8;
3074         }
3075
3076         /* set object offset */
3077         if (ob) {
3078                 obofs[0] = -ob->obmat[3][0];
3079                 obofs[1] = -ob->obmat[3][1];
3080                 obofs[2] = -ob->obmat[3][2];
3081         }
3082         else {
3083                 copy_v3_v3(obofs, rv3d->ofs);
3084         }
3085
3086         /* calc an adjustment based on distance from camera
3087            disabled per patch 14402 */
3088          d = 1.0f;
3089
3090 /*    if (ob) {
3091                 sub_v3_v3v3(diff, obofs, rv3d->ofs);
3092                 d = len_v3(diff);
3093         }
3094 */
3095
3096         reverse = (rv3d->persmat[2][1] < 0.0f) ? -1.0f : 1.0f;
3097
3098         /*----------------------------------------------------
3099          * ndof device pan
3100          */
3101         psens *= 1.0f + d;
3102         curareaX = sbadjust * psens * fval[0];
3103         curareaY = sbadjust * psens * fval[1];
3104         dvec[0] = curareaX * rv3d->persinv[0][0] + curareaY * rv3d->persinv[1][0];
3105         dvec[1] = curareaX * rv3d->persinv[0][1] + curareaY * rv3d->persinv[1][1];
3106         dvec[2] = curareaX * rv3d->persinv[0][2] + curareaY * rv3d->persinv[1][2];
3107         add_v3_v3(rv3d->ofs, dvec);
3108
3109         /*----------------------------------------------------
3110          * ndof device dolly
3111          */
3112         len = zsens * sbadjust * fval[2];
3113
3114         if (rv3d->persp==RV3D_CAMOB) {
3115                 if(rv3d->persp==RV3D_CAMOB) { /* This is stupid, please fix - TODO */
3116                         rv3d->camzoom+= 10.0f * -len;
3117                 }
3118                 if (rv3d->camzoom < minZoom) rv3d->camzoom = minZoom;
3119                 else if (rv3d->camzoom > maxZoom) rv3d->camzoom = maxZoom;
3120         }
3121         else if ((rv3d->dist> 0.001*v3d->grid) && (rv3d->dist<10.0*v3d->far)) {
3122                 rv3d->dist*=(1.0 + len);
3123         }
3124
3125
3126         /*----------------------------------------------------
3127          * ndof device turntable
3128          * derived from the turntable code in viewmove
3129          */
3130
3131         /* Get the 3x3 matrix and its inverse from the quaternion */
3132         quat_to_mat3( m,rv3d->viewquat);
3133         invert_m3_m3(m_inv,m);
3134
3135         /* Determine the direction of the x vector (for rotating up and down) */
3136         /* This can likely be compuated directly from the quaternion. */
3137         mul_m3_v3(m_inv,xvec);
3138         mul_m3_v3(m_inv,yvec);
3139         mul_m3_v3(m_inv,zvec);
3140
3141         /* Perform the up/down rotation */
3142         phi = sbadjust * rsens * /*0.5f * */ fval[3]; /* spin vertically half as fast as horizontally */
3143         q1[0] = cos(phi);
3144         mul_v3_v3fl(q1+1, xvec, sin(phi));
3145         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
3146
3147         if (use_sel) {
3148                 conjugate_qt(q1); /* conj == inv for unit quat */
3149                 sub_v3_v3(rv3d->ofs, obofs);
3150                 mul_qt_v3(q1, rv3d->ofs);
3151                 add_v3_v3(rv3d->ofs, obofs);
3152         }
3153
3154         /* Perform the orbital rotation */
3155         /* Perform the orbital rotation
3156            If the seen Up axis is parallel to the zoom axis, rotation should be
3157            achieved with a pure Roll motion (no Spin) on the device. When you start
3158            to tilt, moving from Top to Side view, Spinning will increasingly become
3159            more relevant while the Roll component will decrease. When a full
3160            Side view is reached, rotations around the world's Up axis are achieved
3161            with a pure Spin-only motion.  In other words the control of the spinning
3162            around the world's Up axis should move from the device's Spin axis to the
3163            device's Roll axis depending on the orientation of the world's Up axis
3164            relative to the screen. */
3165         //phi = sbadjust * rsens * reverse * fval[4];  /* spin the knob, y axis */
3166         phi = sbadjust * rsens * (yvec[2] * fval[4] + zvec[2] * fval[5]);
3167         q1[0] = cos(phi);
3168         q1[1] = q1[2] = 0.0;
3169         q1[3] = sin(phi);
3170         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
3171
3172         if (use_sel) {
3173                 conjugate_qt(q1);
3174                 sub_v3_v3(rv3d->ofs, obofs);
3175                 mul_qt_v3(q1, rv3d->ofs);
3176                 add_v3_v3(rv3d->ofs, obofs);
3177         }
3178
3179         /*----------------------------------------------------
3180          * refresh the screen
3181          */
3182 // XXX    scrarea_do_windraw(curarea);
3183 }
3184 #endif // if 0, unused NDof code
3185
3186 /* give a 4x4 matrix from a perspective view, only needs viewquat, ofs and dist
3187  * basically the same as...
3188  *     rv3d->persp= RV3D_PERSP
3189  *     setviewmatrixview3d(scene, v3d, rv3d);
3190  *     setcameratoview3d(v3d, rv3d, v3d->camera);
3191  * ...but less of a hassle
3192  * */
3193 void view3d_persp_mat4(RegionView3D *rv3d, float mat[][4])
3194 {
3195         float qt[4], dvec[3];
3196         copy_qt_qt(qt, rv3d->viewquat);
3197         qt[0]= -qt[0];
3198         quat_to_mat4(mat, qt);
3199         mat[3][2] -= rv3d->dist;
3200         translate_m4(mat, rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
3201         mul_v3_v3fl(dvec, mat[2], -rv3d->dist);
3202         sub_v3_v3v3(mat[3], dvec, rv3d->ofs);
3203 }