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