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