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