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