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